应用报警设置
图片详情

图片详情

图片详情


常见负载均衡算法
配置中心
服务熔断
服务降级
api网关


vagrant halt
1 | Microsoft Windows [版本 10.0.19043.1165] |
打开资源管理器一看果然没有开启虚拟化


1 | Microsoft Windows [版本 10.0.19043.1165] |
设置自动启动
docker update mysql --restart=always
需要添加nameserver
1 |
|
1 | sudo mkdir -p /etc/docker |
1 | docker run -p 3306:3306 --name mysql \ |
``
docker exec -it mysql mysql -uroot -proot
vi /mydata/mysql/conf/my.cnf
1 | [client] |

使用固定ip需要使用vagrant reload重启虚拟机
但发现不能正确重启
1 | Microsoft Windows [版本 10.0.19043.1165] |
应该是没有正确关机导致的
进入任务管理器,关闭virtual box相关的任务

接下来可以成功启动
1 | Microsoft Windows [版本 10.0.19043.1165] |
docker start重启容器
1 | D:\big\centos>vagrant ssh |
mkdir -p /mydata/redis/conf
echo 'appendonly yes' > /mydata/redis/conf/redis.conf
1 | docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \ |
1 | Microsoft Windows [版本 10.0.19043.1165] |
idea安装mybatisx和lombok插件
开启run-dash board(建议ide配置全局maven环境,否则每一个项目都要重新配置maven环境)
一般不做外键,不然十分耗费数据库性能
办法正确创建
1 | ) ENGINE = InnoDB AUTO_INCREMENT = 53 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '订单' ROW_FORMAT = Dynamic |
1 | 56976 warn optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\fsevents): |
加参数--unsafe-perm
2021年了,还用python2是什么鬼啊

安装sass还是失败
最终解决方案:升级sass的版本参考文献
话说这个项目一年都没有维护了
记得在每个微服务的pom中引入common !
注意dependcy是加到dependencies里面不是加到dependencyManagement里面
http请求的包:apache http component
引入servlet的时候加入<scope>provided</scope>,springboot自带servlet,打包的时候就不用加入了
最新的官方文档
version给的是这个东西<version>mybatis-plus-latest-version</version>
实测会报错,改为<version>3.2.0</version>
1 | Could not find artifact com.baomidou:mybatis-plus:pom:mybatis-plus-latest-version in central |

指定版本:<version>2.3.0.RELEASE</version>
1 | D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-product\src\main\java\com\atguigu\gulimall\product\entity\UndoLogEntity.java:42:13 |

1 | Microsoft Windows [版本 10.0.19043.1165] |
controller的基本结构是:模块名+表名
http://localhost:8080/coupon/coupon/list
在properties加一个映射

这个突然变成5也是很莫名其妙

这里的maven地址也变了

1 | "D:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=4250:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\bin" -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-ware\target\classes;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.5.4\spring-boot-starter-web-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter\2.5.4\spring-boot-starter-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot\2.5.4\spring-boot-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.5.4\spring-boot-autoconfigure-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.5.4\spring-boot-starter-logging-2.5.4.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-classic\1.2.5\logback-classic-1.2.5.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-core\1.2.5\logback-core-1.2.5.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.14.1\log4j-to-slf4j-2.14.1.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar;D:\Users\LND\.m2\repository\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;D:\Users\LND\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Users\LND\.m2\repository\org\yaml\snakeyaml\1.28\snakeyaml-1.28.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.5.4\spring-boot-starter-json-2.5.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.12.4\jackson-databind-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.12.4\jackson-annotations-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.12.4\jackson-core-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.12.4\jackson-datatype-jdk8-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.12.4\jackson-datatype-jsr310-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.12.4\jackson-module-parameter-names-2.12.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.5.4\spring-boot-starter-tomcat-2.5.4.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.52\tomcat-embed-core-9.0.52.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.52\tomcat-embed-el-9.0.52.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.52\tomcat-embed-websocket-9.0.52.jar;D:\Users\LND\.m2\repository\org\springframework\spring-web\5.3.9\spring-web-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-beans\5.3.9\spring-beans-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-webmvc\5.3.9\spring-webmvc-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-aop\5.3.9\spring-aop-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-context\5.3.9\spring-context-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-expression\5.3.9\spring-expression-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter-openfeign\3.0.3\spring-cloud-starter-openfeign-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter\3.0.3\spring-cloud-starter-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-context\3.0.3\spring-cloud-context-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-rsa\1.0.10.RELEASE\spring-security-rsa-1.0.10.RELEASE.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.68\bcpkix-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.68\bcprov-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-openfeign-core\3.0.3\spring-cloud-openfeign-core-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.5.4\spring-boot-starter-aop-2.5.4.jar;D:\Users\LND\.m2\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;D:\Users\LND\.m2\repository\io\github\openfeign\form\feign-form-spring\3.8.0\feign-form-spring-3.8.0.jar;D:\Users\LND\.m2\repository\io\github\openfeign\form\feign-form\3.8.0\feign-form-3.8.0.jar;D:\Users\LND\.m2\repository\commons-fileupload\commons-fileupload\1.4\commons-fileupload-1.4.jar;D:\Users\LND\.m2\repository\commons-io\commons-io\2.2\commons-io-2.2.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-commons\3.0.3\spring-cloud-commons-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-crypto\5.5.2\spring-security-crypto-5.5.2.jar;D:\Users\LND\.m2\repository\io\github\openfeign\feign-core\10.12\feign-core-10.12.jar;D:\Users\LND\.m2\repository\io\github\openfeign\feign-slf4j\10.12\feign-slf4j-10.12.jar;D:\Users\LND\.m2\repository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-common\target\classes;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-boot-starter\3.2.0\mybatis-plus-boot-starter-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus\3.2.0\mybatis-plus-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-extension\3.2.0\mybatis-plus-extension-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-core\3.2.0\mybatis-plus-core-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-annotation\3.2.0\mybatis-plus-annotation-3.2.0.jar;D:\Users\LND\.m2\repository\com\github\jsqlparser\jsqlparser\2.1\jsqlparser-2.1.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis-spring\2.0.2\mybatis-spring-2.0.2.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.5.4\spring-boot-starter-jdbc-2.5.4.jar;D:\Users\LND\.m2\repository\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jdbc\5.3.9\spring-jdbc-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-tx\5.3.9\spring-tx-5.3.9.jar;D:\Users\LND\.m2\repository\org\projectlombok\lombok\1.18.20\lombok-1.18.20.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;D:\Users\LND\.m2\repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;D:\Users\LND\.m2\repository\mysql\mysql-connector-java\8.0.26\mysql-connector-java-8.0.26.jar;D:\Users\LND\.m2\repository\org\springframework\spring-core\5.3.9\spring-core-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jcl\5.3.9\spring-jcl-5.3.9.jar" com.atguigu.gulimall.ware.GulimallWareApplication |

调整lombok版本
<lombok.version>1.18.20</lombok.version>
参考文献
cmd中改为standalone
远程
解决方法参考文献
上面那份解决方法不够完美,可以在common统一管理
加了dependencymanagement可以不用加版本号
properties里写变量
1 | "D:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=7591:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\bin" -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-member\target\classes;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.5.4\spring-boot-starter-web-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter\2.5.4\spring-boot-starter-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot\2.5.4\spring-boot-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.5.4\spring-boot-autoconfigure-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.5.4\spring-boot-starter-logging-2.5.4.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-classic\1.2.5\logback-classic-1.2.5.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-core\1.2.5\logback-core-1.2.5.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.14.1\log4j-to-slf4j-2.14.1.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar;D:\Users\LND\.m2\repository\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;D:\Users\LND\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Users\LND\.m2\repository\org\yaml\snakeyaml\1.28\snakeyaml-1.28.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.5.4\spring-boot-starter-json-2.5.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.12.4\jackson-databind-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.12.4\jackson-annotations-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.12.4\jackson-core-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.12.4\jackson-datatype-jdk8-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.12.4\jackson-datatype-jsr310-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.12.4\jackson-module-parameter-names-2.12.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.5.4\spring-boot-starter-tomcat-2.5.4.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.52\tomcat-embed-core-9.0.52.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.52\tomcat-embed-el-9.0.52.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.52\tomcat-embed-websocket-9.0.52.jar;D:\Users\LND\.m2\repository\org\springframework\spring-web\5.3.9\spring-web-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-beans\5.3.9\spring-beans-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-webmvc\5.3.9\spring-webmvc-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-aop\5.3.9\spring-aop-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-context\5.3.9\spring-context-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-expression\5.3.9\spring-expression-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter-openfeign\3.0.3\spring-cloud-starter-openfeign-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter\3.0.3\spring-cloud-starter-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-context\3.0.3\spring-cloud-context-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-rsa\1.0.10.RELEASE\spring-security-rsa-1.0.10.RELEASE.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.68\bcpkix-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.68\bcprov-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-openfeign-core\3.0.3\spring-cloud-openfeign-core-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.5.4\spring-boot-starter-aop-2.5.4.jar;D:\Users\LND\.m2\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;D:\Users\LND\.m2\repository\io\github\openfeign\form\feign-form-spring\3.8.0\feign-form-spring-3.8.0.jar;D:\Users\LND\.m2\repository\io\github\openfeign\form\feign-form\3.8.0\feign-form-3.8.0.jar;D:\Users\LND\.m2\repository\commons-fileupload\commons-fileupload\1.4\commons-fileupload-1.4.jar;D:\Users\LND\.m2\repository\commons-io\commons-io\2.2\commons-io-2.2.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-commons\3.0.3\spring-cloud-commons-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-crypto\5.5.2\spring-security-crypto-5.5.2.jar;D:\Users\LND\.m2\repository\io\github\openfeign\feign-core\10.12\feign-core-10.12.jar;D:\Users\LND\.m2\repository\io\github\openfeign\feign-slf4j\10.12\feign-slf4j-10.12.jar;D:\Users\LND\.m2\repository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Users\LND\.m2\repository\org\springframework\spring-core\5.3.9\spring-core-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jcl\5.3.9\spring-jcl-5.3.9.jar;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-common\target\classes;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-boot-starter\3.2.0\mybatis-plus-boot-starter-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus\3.2.0\mybatis-plus-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-extension\3.2.0\mybatis-plus-extension-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-core\3.2.0\mybatis-plus-core-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-annotation\3.2.0\mybatis-plus-annotation-3.2.0.jar;D:\Users\LND\.m2\repository\com\github\jsqlparser\jsqlparser\2.1\jsqlparser-2.1.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis-spring\2.0.2\mybatis-spring-2.0.2.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.5.4\spring-boot-starter-jdbc-2.5.4.jar;D:\Users\LND\.m2\repository\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jdbc\5.3.9\spring-jdbc-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-tx\5.3.9\spring-tx-5.3.9.jar;D:\Users\LND\.m2\repository\org\projectlombok\lombok\1.18.20\lombok-1.18.20.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;D:\Users\LND\.m2\repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;D:\Users\LND\.m2\repository\mysql\mysql-connector-java\8.0.26\mysql-connector-java-8.0.26.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-starter-alibaba-nacos-discovery\2.2.5.RELEASE\spring-cloud-starter-alibaba-nacos-discovery-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-alibaba-commons\2.2.5.RELEASE\spring-cloud-alibaba-commons-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-client\1.4.1\nacos-client-1.4.1.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-common\1.4.1\nacos-common-1.4.1.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpasyncclient\4.1.4\httpasyncclient-4.1.4.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpcore-nio\4.4.14\httpcore-nio-4.4.14.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-api\1.4.1\nacos-api-1.4.1.jar;D:\Users\LND\.m2\repository\com\google\guava\guava\24.1.1-jre\guava-24.1.1-jre.jar;D:\Users\LND\.m2\repository\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;D:\Users\LND\.m2\repository\org\checkerframework\checker-compat-qual\2.0.0\checker-compat-qual-2.0.0.jar;D:\Users\LND\.m2\repository\com\google\errorprone\error_prone_annotations\2.1.3\error_prone_annotations-2.1.3.jar;D:\Users\LND\.m2\repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;D:\Users\LND\.m2\repository\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;D:\Users\LND\.m2\repository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;D:\Users\LND\.m2\repository\io\prometheus\simpleclient\0.10.0\simpleclient-0.10.0.jar;D:\Users\LND\.m2\repository\com\alibaba\spring\spring-context-support\1.0.10\spring-context-support-1.0.10.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter-netflix-ribbon\2.2.5.RELEASE\spring-cloud-starter-netflix-ribbon-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-netflix-ribbon\2.2.5.RELEASE\spring-cloud-netflix-ribbon-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-netflix-archaius\2.2.5.RELEASE\spring-cloud-netflix-archaius-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter-netflix-archaius\2.2.5.RELEASE\spring-cloud-starter-netflix-archaius-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\com\netflix\archaius\archaius-core\0.7.6\archaius-core-0.7.6.jar;D:\Users\LND\.m2\repository\commons-configuration\commons-configuration\1.8\commons-configuration-1.8.jar;D:\Users\LND\.m2\repository\com\netflix\ribbon\ribbon\2.3.0\ribbon-2.3.0.jar;D:\Users\LND\.m2\repository\com\netflix\ribbon\ribbon-transport\2.3.0\ribbon-transport-2.3.0.jar;D:\Users\LND\.m2\repository\io\reactivex\rxnetty-contexts\0.4.9\rxnetty-contexts-0.4.9.jar;D:\Users\LND\.m2\repository\io\reactivex\rxnetty-servo\0.4.9\rxnetty-servo-0.4.9.jar;D:\Users\LND\.m2\repository\com\netflix\hystrix\hystrix-core\1.4.3\hystrix-core-1.4.3.jar;D:\Users\LND\.m2\repository\javax\inject\javax.inject\1\javax.inject-1.jar;D:\Users\LND\.m2\repository\io\reactivex\rxnetty\0.4.9\rxnetty-0.4.9.jar;D:\Users\LND\.m2\repository\com\netflix\ribbon\ribbon-core\2.3.0\ribbon-core-2.3.0.jar;D:\Users\LND\.m2\repository\com\netflix\ribbon\ribbon-httpclient\2.3.0\ribbon-httpclient-2.3.0.jar;D:\Users\LND\.m2\repository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;D:\Users\LND\.m2\repository\com\sun\jersey\jersey-client\1.19.1\jersey-client-1.19.1.jar;D:\Users\LND\.m2\repository\com\sun\jersey\jersey-core\1.19.1\jersey-core-1.19.1.jar;D:\Users\LND\.m2\repository\javax\ws\rs\jsr311-api\1.1.1\jsr311-api-1.1.1.jar;D:\Users\LND\.m2\repository\com\sun\jersey\contribs\jersey-apache-client4\1.19.1\jersey-apache-client4-1.19.1.jar;D:\Users\LND\.m2\repository\com\netflix\servo\servo-core\0.10.1\servo-core-0.10.1.jar;D:\Users\LND\.m2\repository\com\netflix\servo\servo-internal\0.10.1\servo-internal-0.10.1.jar;D:\Users\LND\.m2\repository\com\netflix\netflix-commons\netflix-commons-util\0.1.1\netflix-commons-util-0.1.1.jar;D:\Users\LND\.m2\repository\com\netflix\ribbon\ribbon-loadbalancer\2.3.0\ribbon-loadbalancer-2.3.0.jar;D:\Users\LND\.m2\repository\com\netflix\netflix-commons\netflix-statistics\0.1.1\netflix-statistics-0.1.1.jar;D:\Users\LND\.m2\repository\io\reactivex\rxjava\1.3.8\rxjava-1.3.8.jar" com.atguigu.gulimall.member.GulimallMemberApplication |
每个微服务创建自己的命名分组,使用配置分组区分环境,dev,test,pord

隔离开发测试环境

可以直接指定名字+文件类型
参考文献: API网关比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd(转)
spring cloud相比spring boot+nginx有什么优势?
1 | ccess to XMLHttpRequest at 'file:///D:/Users/LND/Desktop/ereaseo/gulimall/ES6/mock/user.json' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https. |

npm init -ynpm install webpack -g
npm install -g @vue/cli-init
vue init webpack appname
@相当于src的根目录
1 | Microsoft Windows [版本 10.0.19043.1237] |
将oss的版本设置为2.1.0.release
1 | <dependency> |
1 | "D:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=2614:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\bin" -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;D:\Users\LND\Desktop\ereaseo\gulimall\renren-fast\target\classes;D:\Users\LND\.m2\repository\com\google\code\gson\gson\2.8.5\gson-2.8.5.jar;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-common\target\classes;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpcore\4.4.13\httpcore-4.4.13.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-starter-alibaba-nacos-discovery\2.2.5.RELEASE\spring-cloud-starter-alibaba-nacos-discovery-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-alibaba-commons\2.2.5.RELEASE\spring-cloud-alibaba-commons-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-client\1.4.1\nacos-client-1.4.1.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-common\1.4.1\nacos-common-1.4.1.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpasyncclient\4.1.4\httpasyncclient-4.1.4.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpcore-nio\4.4.13\httpcore-nio-4.4.13.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-api\1.4.1\nacos-api-1.4.1.jar;D:\Users\LND\.m2\repository\io\prometheus\simpleclient\0.5.0\simpleclient-0.5.0.jar;D:\Users\LND\.m2\repository\com\alibaba\spring\spring-context-support\1.0.10\spring-context-support-1.0.10.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-commons\2.2.5.RELEASE\spring-cloud-commons-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-crypto\5.2.1.RELEASE\spring-security-crypto-5.2.1.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-context\2.2.5.RELEASE\spring-cloud-context-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-loadbalancer\3.0.3\spring-cloud-loadbalancer-3.0.3.jar;D:\Users\LND\.m2\repository\io\projectreactor\reactor-core\3.3.2.RELEASE\reactor-core-3.3.2.RELEASE.jar;D:\Users\LND\.m2\repository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;D:\Users\LND\.m2\repository\io\projectreactor\addons\reactor-extra\3.3.2.RELEASE\reactor-extra-3.3.2.RELEASE.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-starter-alibaba-nacos-config\2.2.5.RELEASE\spring-cloud-starter-alibaba-nacos-config-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter-bootstrap\3.0.3\spring-cloud-starter-bootstrap-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter\3.0.3\spring-cloud-starter-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-rsa\1.0.10.RELEASE\spring-security-rsa-1.0.10.RELEASE.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.68\bcpkix-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.68\bcprov-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter\2.2.4.RELEASE\spring-boot-starter-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot\2.2.4.RELEASE\spring-boot-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.2.4.RELEASE\spring-boot-starter-logging-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.12.1\log4j-to-slf4j-2.12.1.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-api\2.12.1\log4j-api-2.12.1.jar;D:\Users\LND\.m2\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\Users\LND\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Users\LND\.m2\repository\org\yaml\snakeyaml\1.25\snakeyaml-1.25.jar;D:\Users\LND\.m2\repository\org\hamcrest\hamcrest\2.1\hamcrest-2.1.jar;D:\Users\LND\.m2\repository\net\bytebuddy\byte-buddy\1.10.6\byte-buddy-1.10.6.jar;D:\Users\LND\.m2\repository\org\springframework\spring-core\5.2.3.RELEASE\spring-core-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jcl\5.2.3.RELEASE\spring-jcl-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.2.4.RELEASE\spring-boot-starter-web-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.2.4.RELEASE\spring-boot-starter-json-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.10.2\jackson-datatype-jdk8-2.10.2.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.10.2\jackson-datatype-jsr310-2.10.2.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.10.2\jackson-module-parameter-names-2.10.2.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.2.4.RELEASE\spring-boot-starter-tomcat-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.30\tomcat-embed-core-9.0.30.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.30\tomcat-embed-el-9.0.30.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.30\tomcat-embed-websocket-9.0.30.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-validation\2.2.4.RELEASE\spring-boot-starter-validation-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;D:\Users\LND\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.18.Final\hibernate-validator-6.0.18.Final.jar;D:\Users\LND\.m2\repository\org\jboss\logging\jboss-logging\3.4.1.Final\jboss-logging-3.4.1.Final.jar;D:\Users\LND\.m2\repository\org\springframework\spring-web\5.2.3.RELEASE\spring-web-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-webmvc\5.2.3.RELEASE\spring-webmvc-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-expression\5.2.3.RELEASE\spring-expression-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.2.4.RELEASE\spring-boot-starter-aop-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-aop\5.2.3.RELEASE\spring-aop-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar;D:\Users\LND\.m2\repository\org\springframework\spring-context-support\5.2.3.RELEASE\spring-context-support-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-beans\5.2.3.RELEASE\spring-beans-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-context\5.2.3.RELEASE\spring-context-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-data-redis\2.2.4.RELEASE\spring-boot-starter-data-redis-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\data\spring-data-redis\2.2.4.RELEASE\spring-data-redis-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\data\spring-data-keyvalue\2.2.4.RELEASE\spring-data-keyvalue-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\data\spring-data-commons\2.2.4.RELEASE\spring-data-commons-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-tx\5.2.3.RELEASE\spring-tx-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\spring-oxm\5.2.3.RELEASE\spring-oxm-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\io\lettuce\lettuce-core\5.2.1.RELEASE\lettuce-core-5.2.1.RELEASE.jar;D:\Users\LND\.m2\repository\io\netty\netty-common\4.1.45.Final\netty-common-4.1.45.Final.jar;D:\Users\LND\.m2\repository\io\netty\netty-handler\4.1.45.Final\netty-handler-4.1.45.Final.jar;D:\Users\LND\.m2\repository\io\netty\netty-buffer\4.1.45.Final\netty-buffer-4.1.45.Final.jar;D:\Users\LND\.m2\repository\io\netty\netty-codec\4.1.45.Final\netty-codec-4.1.45.Final.jar;D:\Users\LND\.m2\repository\io\netty\netty-transport\4.1.45.Final\netty-transport-4.1.45.Final.jar;D:\Users\LND\.m2\repository\io\netty\netty-resolver\4.1.45.Final\netty-resolver-4.1.45.Final.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-configuration-processor\2.2.4.RELEASE\spring-boot-configuration-processor-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-boot-starter\3.3.1\mybatis-plus-boot-starter-3.3.1.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus\3.3.1\mybatis-plus-3.3.1.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-extension\3.3.1\mybatis-plus-extension-3.3.1.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-core\3.3.1\mybatis-plus-core-3.3.1.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-annotation\3.3.1\mybatis-plus-annotation-3.3.1.jar;D:\Users\LND\.m2\repository\com\github\jsqlparser\jsqlparser\3.1\jsqlparser-3.1.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis\3.5.3\mybatis-3.5.3.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis-spring\2.0.3\mybatis-spring-2.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.2.4.RELEASE\spring-boot-autoconfigure-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.2.4.RELEASE\spring-boot-starter-jdbc-2.2.4.RELEASE.jar;D:\Users\LND\.m2\repository\com\zaxxer\HikariCP\3.4.2\HikariCP-3.4.2.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jdbc\5.2.3.RELEASE\spring-jdbc-5.2.3.RELEASE.jar;D:\Users\LND\.m2\repository\mysql\mysql-connector-java\8.0.17\mysql-connector-java-8.0.17.jar;D:\Users\LND\.m2\repository\com\oracle\ojdbc6\11.2.0.3\ojdbc6-11.2.0.3.jar;D:\Users\LND\.m2\repository\com\microsoft\sqlserver\sqljdbc4\4.0\sqljdbc4-4.0.jar;D:\Users\LND\.m2\repository\org\postgresql\postgresql\42.2.9\postgresql-42.2.9.jar;D:\Users\LND\.m2\repository\com\alibaba\druid-spring-boot-starter\1.1.13\druid-spring-boot-starter-1.1.13.jar;D:\Users\LND\.m2\repository\com\alibaba\druid\1.1.13\druid-1.1.13.jar;D:\Users\LND\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\Users\LND\.m2\repository\org\quartz-scheduler\quartz\2.3.0\quartz-2.3.0.jar;D:\Users\LND\.m2\repository\com\mchange\mchange-commons-java\0.2.11\mchange-commons-java-0.2.11.jar;D:\Users\LND\.m2\repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;D:\Users\LND\.m2\repository\commons-fileupload\commons-fileupload\1.2.2\commons-fileupload-1.2.2.jar;D:\Users\LND\.m2\repository\commons-io\commons-io\2.5\commons-io-2.5.jar;D:\Users\LND\.m2\repository\commons-codec\commons-codec\1.10\commons-codec-1.10.jar;D:\Users\LND\.m2\repository\commons-configuration\commons-configuration\1.10\commons-configuration-1.10.jar;D:\Users\LND\.m2\repository\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-core\1.4.0\shiro-core-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-lang\1.4.0\shiro-lang-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-cache\1.4.0\shiro-cache-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-crypto-hash\1.4.0\shiro-crypto-hash-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-crypto-core\1.4.0\shiro-crypto-core-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-crypto-cipher\1.4.0\shiro-crypto-cipher-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-config-core\1.4.0\shiro-config-core-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-config-ogdl\1.4.0\shiro-config-ogdl-1.4.0.jar;D:\Users\LND\.m2\repository\commons-beanutils\commons-beanutils\1.9.3\commons-beanutils-1.9.3.jar;D:\Users\LND\.m2\repository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-event\1.4.0\shiro-event-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-spring\1.4.0\shiro-spring-1.4.0.jar;D:\Users\LND\.m2\repository\org\apache\shiro\shiro-web\1.4.0\shiro-web-1.4.0.jar;D:\Users\LND\.m2\repository\io\jsonwebtoken\jjwt\0.7.0\jjwt-0.7.0.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.10.2\jackson-databind-2.10.2.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.10.2\jackson-annotations-2.10.2.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.10.2\jackson-core-2.10.2.jar;D:\Users\LND\.m2\repository\com\github\axet\kaptcha\0.0.9\kaptcha-0.0.9.jar;D:\Users\LND\.m2\repository\com\jhlabs\filters\2.0.235\filters-2.0.235.jar;D:\Users\LND\.m2\repository\io\springfox\springfox-swagger2\2.7.0\springfox-swagger2-2.7.0.jar;D:\Users\LND\.m2\repository\io\swagger\swagger-annotations\1.5.13\swagger-annotations-1.5.13.jar;D:\Users\LND\.m2\repository\io\swagger\swagger-models\1.5.13\swagger-models-1.5.13.jar;D:\Users\LND\.m2\repository\io\springfox\springfox-spi\2.7.0\springfox-spi-2.7.0.jar;D:\Users\LND\.m2\repository\io\springfox\springfox-core\2.7.0\springfox-core-2.7.0.jar;D:\Users\LND\.m2\repository\io\springfox\springfox-schema\2.7.0\springfox-schema-2.7.0.jar;D:\Users\LND\.m2\repository\io\springfox\springfox-swagger-common\2.7.0\springfox-swagger-common-2.7.0.jar;D:\Users\LND\.m2\repository\io\springfox\springfox-spring-web\2.7.0\springfox-spring-web-2.7.0.jar;D:\Users\LND\.m2\repository\org\reflections\reflections\0.9.11\reflections-0.9.11.jar;D:\Users\LND\.m2\repository\org\javassist\javassist\3.21.0-GA\javassist-3.21.0-GA.jar;D:\Users\LND\.m2\repository\com\google\guava\guava\18.0\guava-18.0.jar;D:\Users\LND\.m2\repository\com\fasterxml\classmate\1.5.1\classmate-1.5.1.jar;D:\Users\LND\.m2\repository\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;D:\Users\LND\.m2\repository\org\mapstruct\mapstruct\1.1.0.Final\mapstruct-1.1.0.Final.jar;D:\Users\LND\.m2\repository\io\springfox\springfox-swagger-ui\2.7.0\springfox-swagger-ui-2.7.0.jar;D:\Users\LND\.m2\repository\com\qiniu\qiniu-java-sdk\7.2.23\qiniu-java-sdk-7.2.23.jar;D:\Users\LND\.m2\repository\com\squareup\okhttp3\okhttp\3.14.6\okhttp-3.14.6.jar;D:\Users\LND\.m2\repository\com\squareup\okio\okio\1.17.2\okio-1.17.2.jar;D:\Users\LND\.m2\repository\com\aliyun\oss\aliyun-sdk-oss\2.8.3\aliyun-sdk-oss-2.8.3.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpclient\4.5.10\httpclient-4.5.10.jar;D:\Users\LND\.m2\repository\org\jdom\jdom\1.1\jdom-1.1.jar;D:\Users\LND\.m2\repository\com\qcloud\cos_api\4.4\cos_api-4.4.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpmime\4.5.10\httpmime-4.5.10.jar;D:\Users\LND\.m2\repository\org\json\json\20140107\json-20140107.jar;D:\Users\LND\.m2\repository\junit\junit\4.12\junit-4.12.jar;D:\Users\LND\.m2\repository\org\hamcrest\hamcrest-core\2.1\hamcrest-core-2.1.jar;D:\Users\LND\.m2\repository\joda-time\joda-time\2.9.9\joda-time-2.9.9.jar;D:\Users\LND\.m2\repository\com\alibaba\fastjson\1.2.72\fastjson-1.2.72.jar;D:\Users\LND\.m2\repository\cn\hutool\hutool-all\4.1.1\hutool-all-4.1.1.jar;D:\Users\LND\.m2\repository\org\projectlombok\lombok\1.18.20\lombok-1.18.20.jar" io.renren.RenrenApplication |
包路径错误
没有注入到容器中

获取请求体,必须发送POST请求
1)、配置全局的逻辑删除规则(省略)
2)、配置逻辑删除的组件Bean(省略)
3)、加上逻辑删除注解
UPDATE pms_category SET show_status=1
添加dependency
1 | <dependency> |
1 | "D:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=3291:C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar;C:\Users\lnd\.m2\repository\org\junit\platform\junit-platform-launcher\1.7.2\junit-platform-launcher-1.7.2.jar;C:\Users\lnd\.m2\repository\org\apiguardian\apiguardian-api\1.1.0\apiguardian-api-1.1.0.jar;C:\Users\lnd\.m2\repository\org\junit\platform\junit-platform-engine\1.7.2\junit-platform-engine-1.7.2.jar;C:\Users\lnd\.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;C:\Users\lnd\.m2\repository\org\junit\platform\junit-platform-commons\1.7.2\junit-platform-commons-1.7.2.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.2\plugins\junit\lib\junit-rt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-product\target\test-classes;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-product\target\classes;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.5.4\spring-boot-starter-web-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter\2.5.4\spring-boot-starter-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot\2.5.4\spring-boot-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.5.4\spring-boot-autoconfigure-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.5.4\spring-boot-starter-logging-2.5.4.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-classic\1.2.5\logback-classic-1.2.5.jar;D:\Users\LND\.m2\repository\ch\qos\logback\logback-core\1.2.5\logback-core-1.2.5.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.14.1\log4j-to-slf4j-2.14.1.jar;D:\Users\LND\.m2\repository\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar;D:\Users\LND\.m2\repository\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;D:\Users\LND\.m2\repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Users\LND\.m2\repository\org\yaml\snakeyaml\1.28\snakeyaml-1.28.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.5.4\spring-boot-starter-json-2.5.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.12.4\jackson-databind-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.12.4\jackson-annotations-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.12.4\jackson-core-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.12.4\jackson-datatype-jdk8-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.12.4\jackson-datatype-jsr310-2.12.4.jar;D:\Users\LND\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.12.4\jackson-module-parameter-names-2.12.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.5.4\spring-boot-starter-tomcat-2.5.4.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.52\tomcat-embed-core-9.0.52.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.52\tomcat-embed-el-9.0.52.jar;D:\Users\LND\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.52\tomcat-embed-websocket-9.0.52.jar;D:\Users\LND\.m2\repository\org\springframework\spring-web\5.3.9\spring-web-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-beans\5.3.9\spring-beans-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-webmvc\5.3.9\spring-webmvc-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-aop\5.3.9\spring-aop-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-context\5.3.9\spring-context-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-expression\5.3.9\spring-expression-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter-openfeign\3.0.3\spring-cloud-starter-openfeign-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter\3.0.3\spring-cloud-starter-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-context\3.0.3\spring-cloud-context-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-rsa\1.0.10.RELEASE\spring-security-rsa-1.0.10.RELEASE.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.68\bcpkix-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.68\bcprov-jdk15on-1.68.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-openfeign-core\3.0.3\spring-cloud-openfeign-core-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.5.4\spring-boot-starter-aop-2.5.4.jar;D:\Users\LND\.m2\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;D:\Users\LND\.m2\repository\io\github\openfeign\form\feign-form-spring\3.8.0\feign-form-spring-3.8.0.jar;D:\Users\LND\.m2\repository\io\github\openfeign\form\feign-form\3.8.0\feign-form-3.8.0.jar;D:\Users\LND\.m2\repository\commons-fileupload\commons-fileupload\1.4\commons-fileupload-1.4.jar;D:\Users\LND\.m2\repository\commons-io\commons-io\2.2\commons-io-2.2.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-commons\3.0.3\spring-cloud-commons-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\security\spring-security-crypto\5.5.2\spring-security-crypto-5.5.2.jar;D:\Users\LND\.m2\repository\io\github\openfeign\feign-core\10.12\feign-core-10.12.jar;D:\Users\LND\.m2\repository\io\github\openfeign\feign-slf4j\10.12\feign-slf4j-10.12.jar;D:\Users\LND\.m2\repository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-test\2.5.4\spring-boot-starter-test-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-test\2.5.4\spring-boot-test-2.5.4.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-test-autoconfigure\2.5.4\spring-boot-test-autoconfigure-2.5.4.jar;D:\Users\LND\.m2\repository\com\jayway\jsonpath\json-path\2.5.0\json-path-2.5.0.jar;D:\Users\LND\.m2\repository\net\minidev\json-smart\2.4.7\json-smart-2.4.7.jar;D:\Users\LND\.m2\repository\net\minidev\accessors-smart\2.4.7\accessors-smart-2.4.7.jar;D:\Users\LND\.m2\repository\org\ow2\asm\asm\9.1\asm-9.1.jar;D:\Users\LND\.m2\repository\jakarta\xml\bind\jakarta.xml.bind-api\2.3.3\jakarta.xml.bind-api-2.3.3.jar;D:\Users\LND\.m2\repository\jakarta\activation\jakarta.activation-api\1.2.2\jakarta.activation-api-1.2.2.jar;D:\Users\LND\.m2\repository\org\assertj\assertj-core\3.19.0\assertj-core-3.19.0.jar;D:\Users\LND\.m2\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;D:\Users\LND\.m2\repository\org\junit\jupiter\junit-jupiter\5.7.2\junit-jupiter-5.7.2.jar;D:\Users\LND\.m2\repository\org\junit\jupiter\junit-jupiter-api\5.7.2\junit-jupiter-api-5.7.2.jar;D:\Users\LND\.m2\repository\org\apiguardian\apiguardian-api\1.1.0\apiguardian-api-1.1.0.jar;D:\Users\LND\.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;D:\Users\LND\.m2\repository\org\junit\platform\junit-platform-commons\1.7.2\junit-platform-commons-1.7.2.jar;D:\Users\LND\.m2\repository\org\junit\jupiter\junit-jupiter-params\5.7.2\junit-jupiter-params-5.7.2.jar;D:\Users\LND\.m2\repository\org\junit\jupiter\junit-jupiter-engine\5.7.2\junit-jupiter-engine-5.7.2.jar;D:\Users\LND\.m2\repository\org\junit\platform\junit-platform-engine\1.7.2\junit-platform-engine-1.7.2.jar;D:\Users\LND\.m2\repository\org\mockito\mockito-core\3.9.0\mockito-core-3.9.0.jar;D:\Users\LND\.m2\repository\net\bytebuddy\byte-buddy\1.10.22\byte-buddy-1.10.22.jar;D:\Users\LND\.m2\repository\net\bytebuddy\byte-buddy-agent\1.10.22\byte-buddy-agent-1.10.22.jar;D:\Users\LND\.m2\repository\org\objenesis\objenesis\3.2\objenesis-3.2.jar;D:\Users\LND\.m2\repository\org\mockito\mockito-junit-jupiter\3.9.0\mockito-junit-jupiter-3.9.0.jar;D:\Users\LND\.m2\repository\org\skyscreamer\jsonassert\1.5.0\jsonassert-1.5.0.jar;D:\Users\LND\.m2\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;D:\Users\LND\.m2\repository\org\springframework\spring-core\5.3.9\spring-core-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jcl\5.3.9\spring-jcl-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-test\5.3.9\spring-test-5.3.9.jar;D:\Users\LND\.m2\repository\org\xmlunit\xmlunit-core\2.8.2\xmlunit-core-2.8.2.jar;D:\Users\LND\Desktop\ereaseo\gulimall\gulimall-common\target\classes;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-boot-starter\3.2.0\mybatis-plus-boot-starter-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus\3.2.0\mybatis-plus-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-extension\3.2.0\mybatis-plus-extension-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-core\3.2.0\mybatis-plus-core-3.2.0.jar;D:\Users\LND\.m2\repository\com\baomidou\mybatis-plus-annotation\3.2.0\mybatis-plus-annotation-3.2.0.jar;D:\Users\LND\.m2\repository\com\github\jsqlparser\jsqlparser\2.1\jsqlparser-2.1.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;D:\Users\LND\.m2\repository\org\mybatis\mybatis-spring\2.0.2\mybatis-spring-2.0.2.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.5.4\spring-boot-starter-jdbc-2.5.4.jar;D:\Users\LND\.m2\repository\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\spring-jdbc\5.3.9\spring-jdbc-5.3.9.jar;D:\Users\LND\.m2\repository\org\springframework\spring-tx\5.3.9\spring-tx-5.3.9.jar;D:\Users\LND\.m2\repository\org\projectlombok\lombok\1.18.20\lombok-1.18.20.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpcore\4.4.14\httpcore-4.4.14.jar;D:\Users\LND\.m2\repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;D:\Users\LND\.m2\repository\mysql\mysql-connector-java\8.0.26\mysql-connector-java-8.0.26.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-starter-alibaba-nacos-discovery\2.2.5.RELEASE\spring-cloud-starter-alibaba-nacos-discovery-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-alibaba-commons\2.2.5.RELEASE\spring-cloud-alibaba-commons-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-client\1.4.1\nacos-client-1.4.1.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-common\1.4.1\nacos-common-1.4.1.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpasyncclient\4.1.4\httpasyncclient-4.1.4.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpcore-nio\4.4.14\httpcore-nio-4.4.14.jar;D:\Users\LND\.m2\repository\com\alibaba\nacos\nacos-api\1.4.1\nacos-api-1.4.1.jar;D:\Users\LND\.m2\repository\com\google\guava\guava\24.1.1-jre\guava-24.1.1-jre.jar;D:\Users\LND\.m2\repository\com\google\code\findbugs\jsr305\1.3.9\jsr305-1.3.9.jar;D:\Users\LND\.m2\repository\org\checkerframework\checker-compat-qual\2.0.0\checker-compat-qual-2.0.0.jar;D:\Users\LND\.m2\repository\com\google\errorprone\error_prone_annotations\2.1.3\error_prone_annotations-2.1.3.jar;D:\Users\LND\.m2\repository\com\google\j2objc\j2objc-annotations\1.1\j2objc-annotations-1.1.jar;D:\Users\LND\.m2\repository\org\codehaus\mojo\animal-sniffer-annotations\1.14\animal-sniffer-annotations-1.14.jar;D:\Users\LND\.m2\repository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;D:\Users\LND\.m2\repository\io\prometheus\simpleclient\0.10.0\simpleclient-0.10.0.jar;D:\Users\LND\.m2\repository\com\alibaba\spring\spring-context-support\1.0.10\spring-context-support-1.0.10.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-loadbalancer\3.0.3\spring-cloud-loadbalancer-3.0.3.jar;D:\Users\LND\.m2\repository\org\springframework\boot\spring-boot-starter-validation\2.5.4\spring-boot-starter-validation-2.5.4.jar;D:\Users\LND\.m2\repository\org\hibernate\validator\hibernate-validator\6.2.0.Final\hibernate-validator-6.2.0.Final.jar;D:\Users\LND\.m2\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;D:\Users\LND\.m2\repository\org\jboss\logging\jboss-logging\3.4.2.Final\jboss-logging-3.4.2.Final.jar;D:\Users\LND\.m2\repository\com\fasterxml\classmate\1.5.1\classmate-1.5.1.jar;D:\Users\LND\.m2\repository\io\projectreactor\reactor-core\3.4.9\reactor-core-3.4.9.jar;D:\Users\LND\.m2\repository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;D:\Users\LND\.m2\repository\io\projectreactor\addons\reactor-extra\3.4.4\reactor-extra-3.4.4.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\spring-cloud-starter-alibaba-nacos-config\2.2.5.RELEASE\spring-cloud-starter-alibaba-nacos-config-2.2.5.RELEASE.jar;D:\Users\LND\.m2\repository\org\springframework\cloud\spring-cloud-starter-bootstrap\3.0.3\spring-cloud-starter-bootstrap-3.0.3.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\aliyun-oss-spring-boot-starter\1.0.0\aliyun-oss-spring-boot-starter-1.0.0.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\aliyun-context-spring-boot-starter\1.0.0\aliyun-context-spring-boot-starter-1.0.0.jar;D:\Users\LND\.m2\repository\com\alibaba\cloud\alicloud-context\1.0.5\alicloud-context-1.0.5.jar;D:\Users\LND\.m2\repository\com\aliyun\aliyun-java-sdk-edas\2.44.0\aliyun-java-sdk-edas-2.44.0.jar;D:\Users\LND\.m2\repository\com\google\code\gson\gson\2.8.7\gson-2.8.7.jar;D:\Users\LND\.m2\repository\com\aliyun\oss\aliyun-sdk-oss\3.1.0\aliyun-sdk-oss-3.1.0.jar;D:\Users\LND\.m2\repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;D:\Users\LND\.m2\repository\org\jdom\jdom\1.1\jdom-1.1.jar;D:\Users\LND\.m2\repository\com\sun\jersey\jersey-json\1.9\jersey-json-1.9.jar;D:\Users\LND\.m2\repository\org\codehaus\jettison\jettison\1.1\jettison-1.1.jar;D:\Users\LND\.m2\repository\stax\stax-api\1.0.1\stax-api-1.0.1.jar;D:\Users\LND\.m2\repository\com\sun\xml\bind\jaxb-impl\2.2.3-1\jaxb-impl-2.2.3-1.jar;D:\Users\LND\.m2\repository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;D:\Users\LND\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;D:\Users\LND\.m2\repository\org\codehaus\jackson\jackson-core-asl\1.8.3\jackson-core-asl-1.8.3.jar;D:\Users\LND\.m2\repository\org\codehaus\jackson\jackson-mapper-asl\1.8.3\jackson-mapper-asl-1.8.3.jar;D:\Users\LND\.m2\repository\org\codehaus\jackson\jackson-jaxrs\1.8.3\jackson-jaxrs-1.8.3.jar;D:\Users\LND\.m2\repository\org\codehaus\jackson\jackson-xc\1.8.3\jackson-xc-1.8.3.jar;D:\Users\LND\.m2\repository\com\sun\jersey\jersey-core\1.9\jersey-core-1.9.jar;D:\Users\LND\.m2\repository\com\aliyun\aliyun-java-sdk-core\3.4.0\aliyun-java-sdk-core-3.4.0.jar;D:\Users\LND\.m2\repository\org\json\json\20170516\json-20170516.jar;D:\Users\LND\.m2\repository\com\aliyun\aliyun-java-sdk-ram\3.0.0\aliyun-java-sdk-ram-3.0.0.jar;D:\Users\LND\.m2\repository\com\aliyun\aliyun-java-sdk-sts\3.0.0\aliyun-java-sdk-sts-3.0.0.jar;D:\Users\LND\.m2\repository\com\aliyun\aliyun-java-sdk-ecs\4.2.0\aliyun-java-sdk-ecs-4.2.0.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 com.atguigu.gulimall.product.GulimallProductApplicationTests,testUpload |
SPU:Standard Product Unit(标准化产品单元) 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一 个产品的特性。
SKU:Stock Keeping Unit(库存量单位) 即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU 这是对于大型连锁超市 DC(配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每 种产品均对应有唯一的 SKU 号。
决定SKU的叫做销售属性
每个分类下的商品共享规格参数,与销售属性。只是有些商品不一定要用这个分类下全部的属性;
子组件给父组件传递数据,事件机制

近日发现自己3000mhz的内存条在资源管理器里面显示只有2133mhz
原来是因为xmp没开
今日发现内存不够用了,便寻思着需要加一个根内存条
按照华硕官方的建议指导手册的建议来插发现,点亮之后发现并没有成功识别第二根内存条(两根内存条参数完全一致)
后来发现有可能是插的不牢固(参考文献)
发现真的没插牢,插内存条的时候听见「柯登」一声响,就插牢了
后来发现没点亮,主板的错误码是CC,主板说明书里面也没有描述这个错误
将两根内存条交换反插,发现能成功点亮
点亮之后,报下面这个信息

原来是我开了xmp,F1 重新确认一遍xmp的设置就好了(他会警告你最好cpu要带水冷),F10保存设置并退出,就成功了!

关掉虚拟内存
通过systeminfo查看还是有虚拟内存
1 | 物理内存总量: 32,619 MB |


OC: 自动超频
ROG-STRIX-LC-RTX3080TI-O12G-GAMING


显示器有颜色配置文件
红色,生词
绿色,疏意
We become defensive when criticised, and apply negative $\color{red}{\text{stereotypes}}$ to others to boost our own $\color{red}{\text{esteem}}$.
一旦受到批评,我们就会为自己辩护,并将他人定格为消极的老套形 象,以此增强自己的自尊心。
Devoted $\color{green}{\text{concertgoers}}$ who reply that recordings are no substitute for live performance are missing the point.
那些忠诚的$\color{green}{\text{音乐会听众}}$回应说现场演出绝非是唱片所能替代的,可这些听众没有领会到问题的关键点。
Ants keep $\color{red}{\text{predatory}}$ insects away from where their $\color{red}{\text{aphids}}$ feed; Gmail keeps the $\color{red}{\text{spammers}}$ out of our inboxes.
蚂蚁让$\color{red}{\text{食肉昆虫}}$远离$\color{red}{\text{蚜虫}}$进食的地方;谷歌邮箱让$\color{red}{\text{滥发垃圾邮件的人}}$远离我们的收件箱。
At the same time, people continue to treat fire as an event that needs to be wholly controlled and $\color{red}{\text{unleashed}}$ only out of necessity.
与此同时,人们继续把用火视为一种需要全面控制的事件,只有在必要之时才$\color{red}{\text{放出来}}$使用。
As boards $\color{red}{\text{scrutinize}}$ succession plans in response to shareholder pressure, executives who don’t get the nod also may wish to move on.
在董事会迫于股东的压力严格$\color{red}{\text{审查}}$继任计划的时候,那些没被选中的高管们也可能想离开。
Everyone needs to find their extra—their unique value contribution that makes them stand out in whatever is their field of employment.
人人都需要找到自己的额外价值——让自己在任何所在$\color{green}{\text{职业领域}}$中都脱颖而出的独特价值贡献。
It is also the reason why when we try to describe music with words, all we can do is $\color{red}{\text{articulate}}$ our reactions to it, and not grasp music itself.
这也是为什么当我们试图用语言来描述音乐时,我们所能做的只能是$\color{red}{\text{说清楚}}$对音乐的感受,而不能理解音乐本身。
Scientists jumped to the rescue with some distinctly $\color{red}{\text{shaky}}$ evidence to $\color{green}{\text{the effect}}$ that insects would eat us up if birds failed to control them.
科学家们立即拿出某些明显$\color{red}{\text{站不住脚}}$的证据前来“救驾”,其大意是说如果鸟儿不能控制这些昆虫的数量的话,昆虫就会吃光一切。(the effect 后果)
A moralist, satirist, and social reformer, Dickens crafted complex plots and $\color{red}{\text{striking}}$ characters that capture the $\color{red}{\text{panorama}}$ of English society.
作为一位道德家、讽刺作家和社会改革家,狄更斯精心设计了复杂的情节和$\color{red}{\text{引人注目}}$的人物,捕捉了英国社会的$\color{red}{\text{全貌}}$。
Half a century of town and country planning has enabled it to $\color{red}{\text{retain}}$ an enviable rural $\color{red}{\text{coherence}}$, while still permitting low-density urban living.
半个世纪的城乡规划使其(英国)得以$\color{red}{\text{保留}}$令人羡慕的乡村$\color{red}{\text{和谐}}$,同时仍允许低密度的城镇生活。
$\color{red}{\text{Integrity}}$ had collapsed, she argued, because of a collective acceptance that the only “sorting mechanism” in society should be profit and the market.
她认为,$\color{red}{\text{诚信}}$已瓦解,因为我们集体接受的观念是,社会中唯一的“分选机制”应该是利润和市场。
We need them to imagine the United States as a place where they can be productive for a while without committing themselves to staying forever.
我们需要他们把美国想象为这样一个地方,在这里他们可以在一段时间内创造价值,而无需承诺永久居留于此。
The $\color{green}{\text{issue}}$ of $\color{green}{\text{voluntary part-time}}$ relates to $\color{red}{\text{Obamacare}}$ because one of the main purposes was to allow people to get insurance $\color{green}{\text{outside of employment}}$.
$\color{green}{\text{自愿兼职工作}}$这一$\color{green}{\text{问题}}$与奥巴马医改计划相关联,因为该计划的主要目的之一就是让人们$\color{green}{\text{不就业}}$也能得到医疗保险。
Firms are now studying how genes interact, looking for $\color{green}{\text{correlations}}$ that might be used to determine the causes of disease or predict a drug’s $\color{green}{\text{efficacy}}$.
一些公司正在研究基因是如何相互作用的,寻找可能用来确定病因或者预测药物$\color{green}{\text{疗效}}$的$\color{green}{\text{相关性}}$。
$\color{green}{\text{Dead markets}}$ partly reflect the $\color{green}{\text{paralysis}}$ of banks which will not sell assets for fear of $\color{green}{\text{booking losses}}$, yet are reluctant to buy all those $\color{green}{\text{supposed bargains}}$.
$\color{green}{\text{毫无活力的市场}}$一定程度上反映了银行系统的$\color{green}{\text{瘫痪}}$,由于担心$\color{green}{\text{账面损失}}$,银行不会出售资产,但也不愿意收购那些所谓的$\color{green}{\text{廉价资产}}$。
He adds $\color{red}{\text{humbly}}$ that perhaps he was “superior to the $\color{green}{\text{common run of men}}$ in noticing things which easily escape attention, and in observing them carefully”.
他$\color{red}{\text{谦虚地}}$补充道,或许他“优于$\color{green}{\text{常人}}$的地方在于能够注意到容易被忽视的东西,并对这些东西进行仔细观察”。
Many leading American universities want their undergraduates to have a $\color{red}{\text{grounding}}$ in the basic $\color{red}{\text{canon}}$ of ideas that every educated person should $\color{green}{\text{possess}}$.
许多顶尖的美国大学都希望他们的本科生接受对一些基本的、富含思想的经典作品的$\color{red}{\text{基础教学}}$,这些思想是每个受教育人士都$\color{green}{\text{应该有}}$的。($\color{red}{\text{canon}}$:原则)
Buying gifts or giving to charity is often more pleasurable than purchasing things for oneself, and luxuries are most enjoyable when they are consumed $\color{red}}{\text{sparingly}}$.
买礼物或给慈善机构捐款往往会比给自己买东西更让人开心,$\color{red}{\text{有节制地}}$消费奢侈品才会给人以最大的愉悦。
These $\color{red}{\text{benefactors}}$ have succeeded in their chosen fields, they say, and they want to use their wealth to draw attention to those who have succeeded in science.
他们说,这些$\color{red}{\text{捐助者}}$在各自所选择的领域都很成功,而且他们想用自己的财富让人们注意到那些在科学领域有所成就的人。
Perhaps $\color{red}{\text{faintly}}$, they hint that people should look to $\color{red}{\text{intangible}}$ qualities like character and intellect rather than $\color{green}{\text{dieting their way}}$ to $\color{green}{\text{size zero}}$ or $\color{red}{\text{wasp}}$-$\color{red}{\text{waist}}$ $\color{red}{\text{physiques}}$.
这些禁令或许还$\color{red}{\text{隐约地}}$暗示,人们应该注重如个性和才智等$\color{red}{\text{无形}}$的品质,而不是$\color{green}{\text{通过节食}}$来达到“$\color{green}{\text{零号身材}}$”或“$\color{red}{\text{蜂}}$ $\color{red}{\text{腰}}$ $\color{red}{\text{体型}}$”。
The Navy Department moved into the $\color{green}{\text{east wing}}$ in 1879, where $\color{red}{\text{elaborate}}$ wall and ceiling $\color{red}{\text{stenciling}}$ and $\color{red}{\text{marquetry}}$ floors decorated the office of the $\color{green}{\text{Secretary}}$.
海军部门于1879年搬进了$\color{green}{\text{东翼}}$,在那里,$\color{red}{\text{精心制作}}$的墙壁、天花板上的$\color{red}{\text{镂花涂装}}$和$\color{red}{\text{镶嵌工艺}}$地板装饰着$\color{green}{\text{部长}}$办公室。
The researchers mapped not only the city’s vast and $\color{red}{\text{ornate}}$ $\color{green}{\text{ceremonial areas}}$, but also hundreds of simpler $\color{green}{\text{apartment complexes}}$ where common people lived.
研究人员不仅绘制了这座城市广阔且$\color{red}{\text{装饰华丽}}$的$\color{green}{\text{庆典区}}$,还绘制了数百个普通民众居住的简单公寓$\color{green}{\text{建筑群}}$。
It may be said that the measure of the worth of any social institution is its effect in enlarging and improving $\color{green}{\text{experience}}$, but this effect is not a part of its $\color{green}{\text{original motive}}$.
可以说,衡量任何社会制度价值的标准是其在扩大和改进$\color{green}{\text{经验}}$上的成效,但这种成效并不是其$\color{green}{\text{最初动机}}$的一部分。
Our mental health doesn’t really $\color{green}{\text{go anywhere}}$; like the sun behind a cloud, it can be temporarily hidden from view, but it is fully capable of being restored in an instant.
我们的心理健康并不是真的$\color{green}{\text{消失不见}}$了;就像云朵背后的太阳,它也许暂时被遮挡,但是它完全可以在瞬间重焕光芒。
No boy who went to a grammar school could be ignorant that the drama was a form of literature which gave glory to Greece and Rome and might yet bring honor to England.
每个进入文法学校学习的学生都知道,戏剧是一种文学形式,这种文学形式赋予希腊和罗马以荣光,并且可能也会为英国带来荣耀。
The most loyal customers would still get the product they favor, the idea goes, and they’d feel like they were helping $\color{green}{\text{sustain}}$ the quality of something they believe in.
这个想法是这样:那些最忠诚的顾客依旧会购买他们喜欢的产品,他们会觉得这是在帮助$\color{green}{\text{维护}}$他们所信任的产品的品质。
But policymakers who refocus efforts on $\color{green}{\text{improving well-being}}$ rather than simply worrying about GDP figures could avoid the forecasted $\color{green}{\text{doom}}$ and may even see progress.
但是那些重新致力于$\color{green}{\text{改善福祉}}$,而不仅仅是担心国内生产总值数据的决策者们,就能够避免可预见的$\color{green}{\text{厄运}}$,甚至可能看到进步。
Indeed, there is something a little $\color{green}{\text{absurd}}$ in the state getting involved in the planning of such a fundamentally “grassroots” concept $\color{green}{\text{as}}$ community sports associations.
确实,让国家参与$\color{green}{\text{像}}$社区体育协会这样从根本上带有“草根阶层”意味的规划是有些$\color{green}{\text{荒唐}}$的。
While few $\color{green}{\text{craftsmen}}$ or farmers, let alone $\color{green}{\text{dependents}}$ and servants, left $\color{green}{\text{literary compositions}}$ to be analyzed, it is obvious that their views were less fully $\color{green}{\text{intellectualized}}$.
尽管很少有$\color{green}{\text{工匠}}$或农场主能留下可供分析的文学作品,更不用说他们的随从和佣人了,但很明显他们的观点并不十分$\color{green}{\text{理性}}$。
While comment and reaction from lawyers may enhance $\color{green}{\text{stories}}$, it is preferable for journalists to rely on their own notions of significance and make their own judgments.
尽管来自律师们的评论和反馈可能会提高$\color{green}{\text{新闻报道}}$的质量,但新闻记者最好还是依靠自己对事件重要性的认识而做出自己的判断。
Social media allows users to experience news events more $\color{green}{\text{intimately}}$ and immediately while also permitting them to re-share news as a projection of their values and interests.
社交媒体允许用户$\color{green}{\text{更密切}}$、更迅速地体验新闻事件,同时也允许他们将新闻作为自己价值观和兴趣的投射而重新分享。
According to research from Princeton University, people $\color{green}{\text{assess}}$ your $\color{green}{\text{competence}}$, $\color{green}{\text{trustworthiness}}$, and $\color{green}{\text{likeability}}$ in just $\color{green}{\text{a tenth of a second}}$, $\color{green}{\text{solely}}$ based on the way you look.
根据普林斯顿大学的研究,人们会在仅仅$\color{green}{\text{十分之一秒}}$的时间内,$\color{green}{\text{仅}}$根据你的外表去$\color{green}{\text{评判}}$你的$\color{green}{\text{能力}}$、$\color{green}{\text{可信度}}$及你$\color{green}{\text{受人喜欢的程度}}$。
Some $\color{green}{\text{attributed}}$ virtually every important cultural achievement $\color{green}{\text{to}}$ the inventions of a few, especially gifted peoples that, according to $\color{red}{\text{diffusionists}}$, then spread to other cultures.
有些人$\color{green}{\text{认为}}$,几乎每一项重要的文化成就都是由少数特别有天赋的民族所发明创造的,根据$\color{red}{\text{传播论者}}$的看法,这些发明后来传播到了其他的文化中。
The $\color{green}{\text{upside}}$ is the possibilities $\color{green}{\text{contained in}}$ knowing that everything is up to us; where before we were $\color{green}{\text{experts in}}$ the array of limitations, now we become $\color{green}{\text{authorities}}$ of what is possible.
积极的一面是,既然万事都取决于我们,那么就有无限可能。以前,我们能够$\color{green}{\text{熟练应对}}$种种局限;现在,我们$\color{green}{\text{把握着}}$未来的可能。
If people in the network just two $\color{green}{\text{degrees}}$ removed from the initial influential prove $\color{red}{\text{resistant}}$, $\color{green}{\text{for example}}$, the $\color{red}{\text{cascade}}$ of change won’t $\color{red}{\text{propagate}}$ very far or affect many people.
例如,如果在这一社交网络中与最初的影响者存在$\color{green}{\text{两个层级}}$的人们表现出$\color{red}{\text{抵制}}$的话,$\color{green}{\text{那么}}$这$\color{red}{\text{一连串}}$的变化就不会$\color{red}{\text{传播}}$很远或影响许多人。
In a workplace that’s $\color{green}{\text{fundamentally}}$ indifferent to your life and its meaning, office speak can help you figure out how you relate to your work—and how your work defines who you are.
在一个$\color{green}{\text{根本}}$不关心你的生活及其意义的职场中,办公室用语能帮助你理清自己和工作的关系,以及工作对你的身份的定义。
Conversations are links, which means when you have a conversation with a new person a link gets formed and every conversation you have after that moment will strengthen the link.
交谈是一种联系,这意味着当你和一个刚认识的人交谈时,一种联系就形成了,而在那之后的每一次交谈都会强化这一联系。
Researchers measured people’s $\color{red}{\text{cortisol}}$, which is a stress marker, while they were at work and while they were at home and found it higher at what $\color{green}{\text{is supposed to}}$ be a place of $\color{red}{\text{refuge}}$.
研究人员测量了人们在工作和在家时的皮质醇,它是一种压力标志,并发现在家这个$\color{green}{\text{理应是}}$ $\color{red}{\text{庇护所}}$的地方,人们的皮质醇水平更高。
While Washington and Jefferson privately expressed $\color{red}{\text{distaste}}$ for slavery, they also understood that it was part of the political and economic $\color{red}{\text{bedrock}}$ of the country they helped to create.
尽管华盛顿和杰斐逊私下都表达过对奴隶制的$\color{red}{\text{不满}}$,但是他们也明白,奴隶制是他们帮助创建的这个国家的政治和经济$\color{red}{\text{基础}}$的一部分。
When younger kids learn computer science, they learn that it’s not just a confusing, endless string of letters and numbers—but a tool to build apps, or create artwork, or test $\color{green}{\text{hypotheses}}$.
当小孩子们学习计算机科学的时候,他们会发现它并不仅仅是一串令人困惑的、无穷无尽的字母和数字——它还是一种工具,这种工具能编写应用程序、创作艺术作品或测试$\color{green}{\text{假设}}$。
The Industrial Revolution didn’t go so well for Luddites whose jobs were displaced by mechanized $\color{red}{\text{looms}}$, but it eventually raised living standards and created more jobs than it destroyed.
尽管工业革命在工作被机械化$\color{red}}{\text{织布机}}$取代的卢德派分子中进展并不顺利,但它最终提高了生活水平,并创造了比被它摧毁的工作岗位更多的就业机会。
That $\color{green}{\text{ruling}}$ produced an explosion in business-method patent $\color{red}{\text{filings}}$, $\color{green}{\text{initially by}}$ $\color{green}{\text{emerging}}$ internet companies trying to $\color{green}{\text{stake out}}$ $\color{green}{\text{exclusive}}$ rights to specific types of online $\color{green}{\text{transactions}}$.
那项$\color{green}{\text{裁决}}$使得商业方法专利$\color{red}{\text{申请文件(备案)}}$数量激增,$\color{green}{\text{起初}}$只是一些$\color{green}{\text{新兴}}$的网络公司试图$\color{green}{\text{抢占}}$对某些特定类型的在线$\color{green}{\text{交易}}$方法的$\color{green}{\text{独家}}$专有权。
Even though there is plenty of evidence that the quality of the teachers is the most important variable, teachers’ unions have fought against $\color{green}{\text{getting rid of}}$ bad ones and promoting good ones.
虽然有充分的证据表明,教师素质是最重要的可变因素,但是教师工会却反对$\color{green}{\text{开除}}$差教师、提拔好教师。
Yet most ancestry testing only considers a single $\color{red}{\text{lineage}}$, either the Y chromosome inherited through men in a father’s line or $\color{red}{\text{mitochondrial}}$ DNA, which is passed down only from mothers.
然而,大多数血统检测只考虑单一的$\color{red}{\text{血统}}$,要么只考虑来自父亲的男性遗传的Y染色体,要么只考虑从母亲那里遗传的$\color{red}{\text{线粒体}}$DNA。
Moreover, even though humans have been $\color{red}{\text{upright}}$ for millions of years, our feet and back continue to struggle with $\color{red}{\text{bipedal}}$ posture and cannot easily $\color{red}{\text{withstand}}$ $\color{green}{\text{repeated}}$ $\color{red}{\text{strain}}$ $\color{red}{\text{imposed}}$ by $\color{green}{\text{oversize limbs}}$.
此外,尽管人类$\color{red}{\text{直立}}$行走已达数百万年之久,但是我们的双脚和背部仍然在与$\color{red}{\text{两足行走}}$的姿势作斗争,并且很难$\color{red}{\text{承受}}$ $\color{green}{\text{过长的四肢}}$ $\color{red}{\text{施加}}$的$\color{green}{\text{持续}}$ $\color{red}{\text{压力}}$。
While the researchers assumed that the well-structured daily plans would be most effective when it came to the execution of tasks, they were wrong: the detailed daily plans $\color{green}{\text{demotivated}}$ students.
尽管研究人员认为,在执行任务时,详尽的每日目标是最为高效的,但他们错了,详细的每日计划使学生$\color{green}{\text{失去了动力}}$。
While fossil fuels—coal, oil,$\color{red}{\text{ gas—still}}$ generate roughly 85 percent of the world’s energy supply, it’s clearer than ever that the future belongs to renewable sources such as wind and solar.
虽然化石燃料——煤、石油、天然气——仍然占据世界能源供应的85%左右,但比以往任何时候都更明显的是,未来属于风能和太阳能等可再生能源。
Only if the $\color{green}{\text{jobless}}$ arrive at the jobcentre with a CV, register for online job search, and start looking for work will they be $\color{red}{\text{eligible}}$ for benefit—and then they should report weekly rather than fortnightly.
只有当$\color{green}{\text{失业者}}$带着简历来到就业服务中心,注册在线求职,并开始寻找工作,他们才有$\color{red}{\text{资格}}$获得补助金,然后他们应该每周而不是每两周汇报一次求职情况。
It is not that pink is $\color{green}{\text{intrinsically}}$ bad, but it is such a tiny slice of the rainbow and, though it may $\color{red}{\text{celebrate}}$ girlhood in one way, it also repeatedly and firmly $\color{red}{\text{fuses}}$ girls’ identity to appearance.
究其本质,粉红色$\color{green}{\text{本身}}$并没有什么不好,它只不过是彩虹上微小的一抹。虽然从某种程度上来说它$\color{red}{\text{歌颂}}$了少女时代,但它也反复且坚定地把女孩的个性和外表$\color{red}{\text{融合起来。
}}$
Here, Darwinism seems to offer $\color{green}{\text{justification}}$, for if all humans share common origins, it seems reasonable to suppose that cultural diversity could also be traced to more $\color{green}{\text{constrained}}$ beginnings.
在此,达尔文学说似乎给出了$\color{green}{\text{合理化的解释(正当理由)}}$,这是因为如果整个人类有相同的起源,那么我们就有理由认为,文化多样性同样也可以追溯到更为$\color{green}{\text{有限}}$的开端。
If you are working on a word processor, you can take advantage of its capacity to make additions and deletions as well as move entire paragraphs by making just a few simple keyboard commands.
如果你正借助文字处理软件进行工作,只需通过几个简单的键盘指令,你就可以利用它来进行删减、增加或移动整段文字。
This success, coupled with later research showing that memory itself is not genetically determined, led Ericsson to conclude that the act of memorizing is more of a cognitive exercise than an $\color{green}{\text{intuitive}}$ one.
这次的成功,加上后来表明记忆本身不由基因决定的研究,让埃里克森得出结论:记忆行为与其说是一种$\color{green}{\text{直觉性的活动}}$,不如说是一种认知性的活动。
That’s because Congress has always $\color{red}{\text{envisioned}}$ joint federal-state immigration $\color{green}{\text{enforcement}}$ and $\color{green}{\text{explicitly}}$ encourages state officers to share information and cooperate with federal colleagues.
那是因为美国国会一直$\color{red}{\text{希望}}$联邦政府能与州政府联合$\color{green}{\text{执行(执行,实施)移民法案}}$,并$\color{green}{\text{明确}}$鼓励州政府官员与联邦政府的同事加强合作、信息共享。
Ministers should also look at creating greater $\color{green}{\text{certainty}}$ in the $\color{red}{\text{rental environment}}$, which would have a significant impact on the ability of registered providers to $\color{green}{\text{fund new developments}}$ from $\color{green}{\text{revenues}}$.
部长们也应该考虑提高房屋$\color{red}{\text{租赁市场}}$的$\color{green}{\text{稳定性}}$,这对注册供应商从$\color{green}{\text{收入}}$中拨出资金来$\color{green}{\text{进行新的开发会}}$产生重大的影响。
Such $\color{green}{\text{hijacked media}}$ are the opposite of earned media: an asset or $\color{green}{\text{campaign}}$ becomes $\color{red}{\text{hostage}}$ to consumers, other stakeholders, or $\color{green}{\text{activists}}$ who make negative $\color{green}{\text{allegations}}$ about a brand or product.
这种被操纵的媒体和免费媒体完全不同:一项资产或一场$\color{green}{\text{活动( 运动(为社会、商业或政治目的而进行的一系列有计划的活动);战役)}}$受那些对某个品牌或产品有不满$\color{green}{\text{说法(无证据的说法,指控)}}$的消费者、其他利益相关者或$\color{green}{\text{积极分子}}$所$\color{red}{\text{左右(人质)}}$。
The policy follows similar efforts from other journals, after widespread concern that basic mistakes in data analysis are contributing to the irreproducibility of many published research findings.
这项政策效仿了与其他杂志类似的尝试,此前人们普遍担忧数据分析中的基本错误正导致许多已发表的研究结果无法被再现。
They should $\color{red}{\text{exhibit}}$ strong interest and respect for whatever currently interests their $\color{red}{\text{fledgling}}$ adult (as $\color{green}{\text{naive}}$ or ill $\color{green}{\text{conceived}}$ as it may seem) while becoming a partner in exploring options for the future.
对当前让这些$\color{green}{\text{羽翼未丰}}$的成年人感兴趣的任何事物(也许看上去很$\color{green}{\text{幼稚}}$或欠$\color{green}{\text{考虑(想象)}}$周全),父母都应该表现出强烈的兴趣和尊重,同时要成为他们的伙伴,与他们一起探索未来的选择。
In other words, at a time when the working class has turned the country on its political head, frustrated that the opportunity that once defined America is vanishing, one obvious solution is $\color{green}{\text{staring}}$ us in the face.
换句话说,在工人阶级彻底改变这个国家的政治格局,对曾经使美国之所以成为美国的机会正在消失而感到沮丧的时候,一个显而易见的解决方案就$\color{green}{\text{摆( 盯着看; 凝视; 注视; )}}$在我们面前。
Brain researchers have discovered that when we $\color{green}{\text{consciously}}$ develop new habits, we create parallel $\color{green}{\text{synaptic}}$ paths, and even entirely new brain cells, that can jump our trains of thought onto new, innovative tracks.
大脑研究人员发现,当我们$\color{green}{\text{有意识地}}$培养新习惯的时候,大脑会创建出平行的$\color{green}{\text{突触( 突触的; 与突触有关的; )}}$路径,甚至是全新的脑细胞,这样可以使我们的思路跳转到全新的、创造性的轨道中。
This type of $\color{green}{\text{integrity}}$ requires $\color{green}{\text{well-enforced}}$ laws in government transparency, such as records of official meetings, rules on $\color{green}{\text{lobbying}}$, and information about each elected leader’s source of wealth.
这种$\color{green}{\text{廉政要求}}$在政府透明度方面有$\color{green}{\text{严格执行}}$的法律,如官方会议记录、$\color{green}{\text{游说}}$规则以及每位当选领导人的财富来源的信息。
In the past couple of weeks a $\color{green}{\text{quarrel}}$ has illustrated the value to advertisers of such $\color{green}{\text{fine-grained}}$ information: Should advertisers assume that people are happy to be tracked and sent behavioural ads?
在过去几周里,一场$\color{green}{\text{争论}}$已经阐明了这种$\color{green}{\text{精确的信息}}$对于广告商的价值:广告商是否可以认为用户愿意被追踪其在网络上的行为并接收基于他们在网络上的行为而制定的广告?
Last year, the Transportation Security Administration (TSA) found in a secret check that $\color{green}{\text{undercover investigators}}$ were able to $\color{green}{\text{sneak}}$ weapons—both fake and real—past airport security nearly every time they tried.
去年,美国运输安全管理局,在一次秘密检查中发现,$\color{green}{\text{便衣调查员}}$几乎每次尝试$\color{green}{\text{私携( 偷偷地走; 溜; 偷偷地做; 偷带; 偷拿; 偷走(不重要的或小的东西); )}}$武器——无论是伪造的武器还是真的武器——都能顺利通过机场安检。$\color{green}{\text{undercover: 秘密工作的; 暗中做的; 私下进行的; }}$
California has asked the $\color{red}{\text{justices}}$ to $\color{green}{\text{refrain}}$ from a $\color{green}{\text{sweeping}}$ $\color{green}{\text{ruling}}$, particularly one that upsets the old assumptions that authorities may search through the possessions of suspects at the time of their arrest.
加利福尼亚州已经要求$\color{red}{\text{法官们}}$ $\color{green}{\text{避免}}$做出$\color{green}{\text{一刀切}}$的$\color{green}{\text{裁决}}$,尤其是不能做出那种颠覆了当局在逮捕嫌疑人时可以搜查其财产这些存在已久的假定的裁决。
Priestly explains how the deep blue color of the assistant’s sweater $\color{green}{\text{descended}}$ over the years from fashion shows to department stores and to the bargain bin in which the poor girl doubtless found her $\color{green}{\text{garment}}$.
普里斯特利解释了这位助理身上这件针织衫所采用的深蓝色这些年来是如何从时装秀$\color{green}{\text{没落到( (人)是…的后代的,为…的后裔的; (动物)由…演变而来的,为…的变种的; )}}$百货商店,最后沦落到商品打折处理区的,而这位可怜的女孩无疑是在那里淘到了这件$\color{green}{\text{衣服}}$。
If the $\color{green}{\text{district}}$ is essentially giving a pass to students who do not do their homework because of complicated family lives, it is going riskily close to the implication that standards need to be lowered for poor children.
如果该$\color{green}{\text{学区}}$让那些因为家庭环境复杂而不做家庭作业的学生通过考试的话,那么这就危险地近乎于暗示着,对于贫穷的孩子,学业标准需要降低。
The findings of a research institution have $\color{red}{\text{consistently}}$ shown that workers in all countries can be trained on the job to achieve radically higher productivity and, as a result, $\color{red}{\text{radically}}$ higher standards of living.
一所研究机构的研究结果$\color{red}{\text{一致}}$表明,所有国家的工人都可以通过在岗培训,从$\color{red}{\text{根本上}}$提高生产率,从而从根本上提高生活水平。
Their analysis $\color{green}{\text{ruled out}}$ the possibility that it was firms’ political influence, rather than their CSR stand, that accounted for the $\color{red}{\text{leniency}}$: Companies that contributed more to political campaigns did not receive lower $\color{green}{\text{fines}}$.
他们的分析$\color{green}{\text{排除了}}$这样的可能性,即:是公司的政治影响力,而非他们的企业社会责任立场让公司获得了$\color{red}{\text{宽大}}$处理,因为那些支持政治运动更多的公司并没有被处以更少的$\color{green}{\text{罚金}}$。
The potential evolution of today’s technology, and its social consequences, is $\color{red}{\text{dazzlingly}}$ complicated, and it’s perhaps best left to science fiction writers and $\color{green}{\text{futurologists}}$ to explore the many possibilities we can envisage.
当今科技的潜在发展及其社会影响惊人地复杂,或许我们最好把诸多可能留给科幻作家和$\color{green}{\text{未来学家}}$去探索。
The company, a major energy supplier in New England, $\color{red}{\text{provoked}}$ justified $\color{red}{\text{outrage}}$ in Vermont last week when it announced it was $\color{red}{\text{reneging}}$ on a longstanding commitment to $\color{red}{\text{abide}}$ by the strict nuclear regulations.
当上周新英格兰地区的主要能源供应商宣布它将$\color{red}{\text{放弃}}$ $\color{red}{\text{遵守}}$ 严格的核安全条例这一长期承诺时,该公司在佛蒙特州$\color{red}{\text{激起(provoked)}}$了民众无可厚非的$\color{red}{\text{愤怒}}$。
Of all the changes that have taken place in English-language newspapers during the past quarter-century, perhaps the most far-reaching has been the $\color{red}{\text{inexorable}}$ decline in the scope and seriousness of their arts coverage.
在过去的25年英文报纸所发生的变化中,影响最深远的可能是其艺术方面的报道在范围和严肃程度上都 $\color{red}{\text{不可阻挡}}$ 地下降了。
Infants are $\color{red}{\text{wired}}$ to look at parents’ faces to try to understand their world, and if those faces are $\color{green}{\text{blank}}$ and $\color{green}{\text{unresponsive}}$ —as they often are when $\color{green}{\text{absorbed in a device}}$ —it can be extremely $\color{red}{\text{disconcerting}}$ for the children.
婴幼儿天生会观察父母的表情,试图理解他们的世界,如果父母的脸上 $\color{green}{\text{毫无表情}}$ 和 $\color{green}{\text{反应}}$ —— $\color{green}{\text{沉浸于电子设备}}$ 时经常如此——这会让孩子们极其 $\color{red}{\text{不安}}$ 。
Scientists have found that although we are $\color{green}{\text{prone}}$ to $\color{red}{\text{snap}}$ overreactions, if we take a moment and think about how we are likely to react, we can reduce or even eliminate the negative effects of our quick, $\color{green}{\text{hard-wired}}$ responses.
科学家们已经发现:虽然我们 $\color{green}{\text{易于( 有做…倾向的; 俯卧的; 易于遭受; 有做(坏事)的倾向; 易于遭受…的; )}}$ $\color{red}{\text{快速}}$ 做出过度反应,但是如果我们花点时间考虑一下我们可能会做何反应,就可以减少,甚至是消除我们快速、 $\color{green}{\text{本能}}$ 的反应所带来的消极影响。
Further $\color{green}{\text{arrangements}}$ —and there may be many—between the NHS and DeepMind will be carefully scrutinised to ensure that all necessary permissions have been asked of patients and all unnecessary data has been cleaned.
英国国民医疗服务体系(NHS)和DeepMind之间的进一步的协议——也许还有很多 $\color{green}{\text{协议}}$ ——将受到仔细审查,以确保从病人那里获得了所有必要的许可,以及所有不必要的数据都已被清除。
Studies of both animals and humans have shown that sex hormones somehow affect the stress response, causing females under stress to produce more of the trigger chemicals than do males under the same conditions.
对动物和人类的研究表明:性激素会以某种方式影响应激反应,使处于压力下的雌性比处于相同条件下的雄性产生更多的能触发不良反应的化学物质。
“Carry a book with you at all times” can actually work, too—providing you dip in often enough, so that reading becomes the $\color{green}{\text{default state}}$ from which you temporarily surface to take care of business, before dropping back down.
如果你能经常翻阅的话,“随时携带一本书”这种方式也能奏效,从而让阅读成为你的 $\color{green}{\text{常态}}$ ,你可以在需要处理事务的时候从书中暂时抽离出来,之后再重新开始阅读。
Today, widespread social pressure to immediately go to college in conjunction with increasingly high expectations in a fast-moving world often causes students to completely $\color{green}{\text{overlook}}$ the possibility of taking a gap year.
如今,高中毕业后即刻升入大学这一普遍的社会压力,加之快速发展的世界对学生寄予越来越高的期望,这常常导致学生完全 $\color{green}{\text{忽略}}$ 了选择间隔年这一可能。
It could be that we are evolving two communities of social scientists: one that is $\color{green}{\text{discipline-oriented}}$ and publishing in highly specialized journals, and one that is problem-oriented and publishing elsewhere, such as $\color{green}{\text{policy briefs}}$ .
这可能是因为我们发展出了两类社会科学家群体:一类是 $\color{green}{\text{学科导向型}}$ 并在高度专业的期刊上发表文章,另一类是问题导向型并在如 $\color{green}{\text{政策简报}}$ 等其他地方发表文章。
Steelworkers, airline employees, and now those in the auto industry are joining millions of families who must worry about interest rates, stock market fluctuation, and the harsh reality that they may $\color{green}{\text{outlive}}$ their retirement money.
炼钢工人、航空公司职员,以及现在那些在汽车行业的员工都正在加入那些不得不担心利率、股市波动和退休金 $\color{green}{\text{不够用( 比…活得长; (在…结束或消失后)继续存在; )}}$ 这一残酷现实的数百万家庭的行列。
After all, four decades of evidence has now shown that corporations in Europe as well as the US are $\color{red}{\text{evading}}$ the $\color{red}{\text{meritocratic}}$ hiring and promotion of women to top position—no matter how much “soft pressure” is put upon them.
毕竟四十年的事实现已表明,不管被施加多大的“软压力”,欧洲和美国的企业一直在 $\color{red}{\text{回避}}$ $\color{red}{\text{英才}}$ 管理制度,限制女性晋升到高层。
His analysis should therefore end any self- $\color{green}{\text{contentedness}}$ among those who may believe that the global position of English is so stable that the young generation of the United Kingdom do not need additional language capabilities.
有些人可能认为英语的全球地位如此稳定以至于英国的年轻一代不需要获得额外的语言能力;他的分析应该会结束那些人的 $\color{green}{\text{自满}}$ 情绪。
The Internet—and pressure from funding agencies, who are questioning why commercial publishers are making money from government-funded research by restricting access to it—is making access to scientific results a reality.
提供资金的机构施加压力,质疑为什么商业出版商可以通过限制人们查看政府资助的研究结果而从中牟利,来自这方面的压力和互联网正在使阅读科研结果成为现实。
There is pressure for change from $\color{green}{\text{within the profession}}$ , but opponents of change among the $\color{red}{\text{regulators}}$ insist that keeping $\color{red}{\text{outsiders}}$ out of a law firm $\color{green}{\text{isolates}}$ lawyers $\color{green}{\text{from}}$ the pressure to make money rather than serve clients ethically.
在 $\color{green}{\text{行业内部}}$ 存在着改革的压力,但是 $\color{red}{\text{监管部门}}$ 中反对变革的人坚称,将 $\color{red}{\text{外部人士}}$ 排除在律师事务所之外,可以让律师 $\color{green}{\text{远离}}$ 赚钱的压力,从而遵守职业道德为委托人服务。
The force of geographic conditions $\color{red}{\text{peculiar}}$ to America, the $\color{red}{\text{interplay}}$ of the varied national groups upon one another, and the $\color{red}{\text{sheer}}$ difficulty of maintaining old-world ways in a raw, new continent caused significant changes.
美国 $\color{red}{\text{特有的}}$ 地理条件的影响,不同民族之间的相互作用,以及在这片原始新大陆上维持旧有方式的困难,这些因素引起了重大的变化。
Yet, when one looks at the photographs of the garden created by the homeless, it strikes one that, for all their diversity of styles, these gardens speak of various other fundamental $\color{green}{\text{urges}}$ , beyond that of decoration and creative expression.
然而,当人们看到那些无家可归者所创建的花园的照片时,受到了深深的震撼:尽管这些花园风格多样,但它们除了表现出创作者的装饰和创造力表达的需求之外,还表现出各种其他根本的 $\color{green}{\text{需求}}$ 。
Under the plan, for example, the agency said it would not $\color{red}{\text{prosecute}}$ landowner or businesses that unintentionally kill, harm, or disturb the bird, as long as they had signed a range-wide management plan to restore $\color{red}{\text{prairie}}$ chicken habitat.
例如,根据此项计划,只要他们签署了一项大范围的管理计划来恢复小草原松鸡的栖息地,该管理局称其不会 $\color{red}{\text{起诉}}$ 那些无意杀死、伤害或干扰小 $\color{red}{\text{草原}}$ 松鸡的土地所有者或企业。
In his article “How Intelligent Is Intelligence Testing?”, Sternberg notes that traditional tests best assess analytical and verbal skills but fail to measure creativity and practical knowledge, $\color{green}{\text{components}}$ also critical to problem solving and life success.
在斯腾伯格的文章《智力测试有多明智?》中,他指出传统的测试能最大程度地评估分析能力和语言表达能力,但不能衡量创造力和实践知识, $\color{green}{\text{这些部分}}$ 对于解决问题和获得人生成功也极其重要。
At a time when Thomas Piketty and other economists are warning of rising inequality and the increasing power of inherited wealth, it is bizarre that wealthy $\color{red}{\text{aristocratic}}$ families should still be the symbolic heart of modern democratic states.
在托马斯·皮凯蒂和其他经济学家提醒民众警惕不断加剧的不平等现象和不断增加的继承财富的权力时,这些富有的 $\color{red}{\text{贵族}}$ 家庭仍然是现代民主国家的象征性核心,这是很奇怪的。
Calls to $\color{red}{\text{disassemble}}$ all telescopes on Mauna Kea or to ban future development there ignore the reality that astronomy and Hawaiian culture both seek to answer big questions about who we are, where we come from and where we are going.
$\color{red}{\text{拆除}}$ 莫纳克亚山上所有的望远镜或禁止未来在那里新建望远镜的呼声忽略了这样一个事实,即天文学和夏威夷文化都在寻求关于“我们是谁”“我们来自哪里”“我们要去何处”这些重大问题的答案。
Humans are unique in their capacity to not only make tools but then turn around and use them to create $\color{red}{\text{superfluous}}$ material goods—paintings, $\color{red}{\text{sculpture}}$ and architecture—and superfluous experiences—music, literature, religion and philosophy.
人类的独特之处在于,他们不仅有能力制造工具,而且还能反过来使用工具来创作 $\color{red}{\text{额外}}$ 的有形物品——绘画、 $\color{red}{\text{雕塑}}$ 和建筑——和额外的精神体验——音乐、文学、宗教和哲学。
As a discovery claim works its way through the community, the interaction and $\color{red}{\text{confrontation}}$ between shared and competing beliefs about the science and the technology involved transforms an individual’s discovery claim into the community’s credible discovery.
当一个发现声明逐步通过科学界的审查时,与该科技相容和矛盾的观点就会相互作用和 $\color{red}{\text{对抗 (n.对抗; 对峙; 冲突;)}}$,这样就会把个人的发现声明转变为科学界的可靠发现。
But in her new book Join the Club, Tina Rosenberg $\color{red}{\text{contends}}$ that peer pressure can also be a positive force through what she calls the social cure, in which organizations and officials use the power of group dynamics to help individuals improve their lives and possibly the world.
但是,蒂娜·罗森堡在她的新书《加入俱乐部》中 $\color{red}{\text{主张(v.(尤指在争论中)声称,主张,认为; 竞争; 争夺)}}$ ,同辈压力也可以通过她所说的“社会治疗”转化成一种积极的力量。在社会治疗的过程中,各机构及官员可以利用群体动力来帮助个人改善生活,甚至可能改善整个世界。
Fundamentally, the USPS is in a historic $\color{green}{\text{squeeze}}$ between technological change that has permanently decreased demand for its bread-and-butter product, first-class mail, and a regulatory structure that denies management the flexibility to adjust its operations to the new reality.
从根本上说,美国邮政署(USPS)正处于一个历史性的 $\color{green}{\text{困境}}$ 之中,一方面是技术变革永久性地降低了对其主要产品——普通邮件的需求,另一方面是监管结构拒绝让管理部门灵活调整其业务以适应新形势。
Unhappy parents rarely are provoked to wonder if they shouldn’t have had kids, but unhappy childless folks are bothered with the message that children are the single most important thing in the world: obviously their misery must be a direct result of the gaping baby-size holes in their lives.
几乎没有事情会促使不幸福的父母去琢磨自己是否不该养孩子,但是不幸福的且没有孩子的人们却总是被“孩子是世上唯一最重要的东西”这一信息所困扰:显然他们的不幸肯定是他们一生中没有孩子的缺憾造成的。
To encourage innovation and competition, the report calls for increased investment in research, the crafting of coherent curricula that improve students’ ability to solve problems and communicate effectively in the 21st century, increased funding for teachers and the encouragement of scholars to bring their learning to bear on the great challenges of the day.
为了鼓励创新和竞争,报告呼吁增加对此项研究的投资,呼吁精心设计系统连贯的课程以提高学生在21世纪解决问题和有效沟通的能力,呼吁增加教师的经费,并鼓励学者应用他们的学识来应对当今巨大的挑战。
Moreover, average overall margins are higher in wholesale than in retail; wholesale demand from the food service sector is growing quickly as more Europeans eat out more often; and changes in the competitive dynamics of this fragmented industry are at last making it feasible for wholesalers to consolidate.
此外,批发业的平均总利润高于零售业;随着越来越多的欧洲人更加频繁地外出就餐,餐饮服务业的批发需求也迅速增长;这一零散产业竞争力量的变化最终会使批发商们的联合成为可能。
Just as bosses and boards have finally sorted out their worst accounting and compliance troubles, and improved their feeble corporation governance, a new problem threatens to earn them—especially in America—the sort of nasty headlines that inevitably lead to heads rolling in the executive suite: data insecurity.
就在老板和董事会终于处理好其最严重的财务和规章问题,改善了公司薄弱的管理之后,又一个新问题正威胁着他们——尤其是在美国——这个问题就是数据不安全性,这会让他们出现在令人不快的新闻头条中,将不可避免地使高层们受到严惩。
The article is actually quite optimistic, as it outlines a potential solution to this problem, suggesting that an approach (which involves a one-hour, next-to-no-cost program) can close 63 percent of the achievement gap (measured by such factors as grades) between first-generation and other students.
这篇文章实际上相当乐观,因为它针对这个问题简要描述了一种可能的解决方案,表示有一种方式(一个耗时一小时、几乎零成本的项目)能够缩小第一代大学生和其他学生之间63%的成绩差距(以分数等指标衡量)。
Precisely because readers from different historical periods, places and social experiences produce different but overlapping readings of the same words on the page—including for texts that engage with fundamental human concerns—debates about texts can play an important role in social discussion of beliefs and values.
正因为来自不同历史时期、不同地域和有着不同社会经历的读者会对页面上那些相同的文字产生不同但有重叠的解读——包括对涉及人类所关注的基本问题的文本解读,关于文本解读的争议才能在信仰和价值观的社会讨论中发挥重要作用。
At Tulane University’s Tear Analysis Laboratory, Dr. Peter Kastl and his colleagues report that they can use tears to detect drug abuse and exposure to medication, to determine whether a contact lens fits properly or why it may be uncomfortable, to study the causes of “dry eye” syndrome and the effects of eye surgery, and perhaps even to measure exposure to environmental pollutants.
在杜兰大学的眼泪分析实验室,彼得·卡斯尔博士和他的同事报告称,他们可以用眼泪来检测出滥用毒品及使用药物的情况,确认隐形眼镜戴起来是否合适或者是戴着不舒适的原因,还能用来探究“干眼”综合征产生的原因和眼部手术的效果,也许甚至还能用来测量与环境污染物接触的情况。
Young people who are still getting started in life were more likely than older adults to $\color{red}{\text{prioritize}}$ personal fulfillment in their work, to believe they will advance their careers most by regularly changing jobs, to $\color{green}{\text{favor}}$ communities with more public services and a faster pace of life, to agree that couples should be financially secure before getting married or having children, and to maintain that children are best served by two parents working outside the home, the survey found.
该调查发现,比起老年人,那些仍然处在生活起跑线的年轻人会更优先考虑他们在工作中的个人成就,更加认同通过定期换工作来推进个人职业生涯,更喜欢拥有较多公共服务的社区和节奏更快的生活,更坚信夫妻在结婚或者抚育孩子之前应该有经济上的保障,更主张父母双方都在外工作才能给孩子提供最好的生活。

1 |
|
1 | // |
1 |

需要考虑到多项式的值为0的时候,输出为"0"不能有空格"0 "
测试数据中的某个测试点的输出格式错误的测试数据
记得考虑到所有的值都被round了
对应到我的代码中:这个地方也需要round
测试数据中的测试点1
$\xrightarrow{\text{summit}}$读取输入的字符串$\xrightarrow{\text{rectify_data}}$得到的中间数据结构$\xrightarrow{\text{cal_poly}}$得到中间数据结构表示的计算的结果$\xrightarrow{\text{rectify_str}}$得到符合题目要求的正确结果$\xrightarrow{\text{summit}}$输出
1 | from typing import Dict |
1 | // |
1 | [ |


错误原因没有考虑经过$\color{green}{\text{前面一个点}}$到$\color{red}{\text{现在这个点}}$需要加上到$\color{green}{\text{前面一个点}}$的路径数(见data.json中的第三个测试数据)
.jpg)


做一次dijkstra,删一次路径
1 | from typing import Dict, List |
1 | [ |

The input ends with N being 0. That case must NOT be processed.?
相较于java的动态特性,以及对c++的不熟悉,在设计树的数据结构的时候束手束脚的,加上pta1002超时的教训,意味着作为一种算法考试,为了规避超时,在延展性,设计性,优雅性上做出牺牲
在commit(#f2270f5)中进行了不好的设计(但貌似可以用时间换空间),应该让栈去记录层数信息而不是让树这个数据结构本身去记录层数信息

只生成空vector

生成整个树的结构,不进行队列的操作


同样的思路,python好好的
果然没能好好掌握c++呢

1 | import unittest |
1 | // |
1 | // |
1 | [ |


问题需要解决
1 | from typing import List |


原因(#284a809)的版本多加了个{}

用signIn 来计算SignOut了
1 | from typing import List |
1 | [ |

${\textstyle\unicode{x2460}}$ 将一个[1,-1,2](data.json中id=32的测试数据),按照正负拆分为子列表[[1],[-1],[2]]。
$\color{red}{\text{正数列表}}$:
[1],[2]
$\color{green}{\text{负数列表}}$:[-1]
${\textstyle\unicode{x2461}}$ 两个$\color{red}{\text{正数列表}}$中间有$\color{green}{\text{负数列表}}$的话肯定会让整个序列减少,但如果这三个列表的和(两个$\color{red}{\text{正数列表}}$中间有$\color{green}{\text{负数列表}}$)等于或大于另外两个$\color{red}{\text{正数列表}}$单独的和,那么合并这三个序列成更大的列表。
${\textstyle\unicode{x2462}}$ 处理完之后,找到最大的序列并,输出题目要求的内容。
将思路翻译成程序,并考虑边界情况。

通过data.json的1-6

通过data.json的所有测试


让merge_positive在result更新之后,记住merge的位置,不再从0开始扫描,不再TLE
发现原来是题目理解错了,他要求的不是最长的序列。$\color{red}{\text{读题很重要}}$
1 | from typing import List |
1 | [ |

第一个不是层数
1 | def summit(): |
1 | [ |
Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The answer is yes, if 6 is a decimal number and 110 is a binary number.Now for any pair of positive integers $N_1$ and $N_2$, your task is to find the radix of one number while that of the other is given.
Each input file contains one test case. Each case occupies a line which contains 4 positive integers:N1 N2 tag radixHere N1 and N2 each has no more than 10 digits. A digit is less than its radix and is chosen from the set { 0-9, a-z } where 0-9 represent the decimal numbers 0-9, and a-z represent the decimal numbers 10-35. The last number radix is the radix of N1 if tag is 1, or of N2 if tag is 2.
For each test case, print in one line the radix of the other number so that the equation N1 = N2 is true. If the equation is impossible, print Impossible. If the solution is not unique, output the smallest possible radix.
1 | 6 110 1 10 |
1 | 2 |
1 | 1 ab 1 2 |
1 | Impossible |
可以参考2分法的做法
参考文献




使用二分查找

1 | from typing import Tuple |
1 | [ |

没搞懂题目在干什么,但好像是每行套公式取最值

1 | from functools import reduce |
1 | [ |




神坑!相同分数的人排名相同,但下一个新排位是他前面的人的数量!

1 | from typing import Dict, List |
1 | // |
1 | // |
1 | [ |

需要几次dfs就有几个连通分量,连通分量-1等于需要加的边的个数,就是要修的桥的数量

为什么反而错了一个?
按照严书优化的代码



使用set的速度

1 | from typing import List, Callable, Dict, Set |
1 | // |
1 | [ |

8:00时候的时间戳为0,之后每一分钟时间戳+1
按值删除元素list.remove

神坑:17点被前台服务的都要服务完

1 | from typing import List, Dict |
1 | // |
1 | [ |
1 | def summit(): |
1 | [ |

怎么paired的也没说,只能猜,一个个查询,直到找到能paired的
已经预料到会超时了。。

一行代码磨一年。。。continue和break 引起的血案


纠结cin了好久,看到网上cin也是写的很难看,感觉确实不能一股脑cin就完事了?

原因sort不能用于list,但是可以用于vector,并且line65对one的排序不影响record
如果需要清空缓存要使用,参考文献
stringstream.str(“”);


1 | from typing import List, Dict |
1 | // |
1 | [ |

首先保证带权路径最小,然后保证带去的自行车的数量最少,然后保证带回来的自行车的数量最少
Q:必须保证保证那条路径上所有的单车都是只有一半的状态的嘛?还是只需要调整问题节点
A:保证保证那条路径上所有的单车都是只有一半的状态,每一个站点超过就带回去,不够就带过来
变种dijkstra,递归找到所有的路径
在dijkstra存储结构上,

只adjust有问题的station



还不如直接正着dfs



1 | from typing import List, Dict |
1 | // |
1 | [ |

不能使用namedtuple,tuple是不可变的
slot的使用,参考文献

1 | C:\Users\lnd\anaconda3\lib\site-packages\urllib3\connectionpool.py:1013: InsecureRequestWarning: Unverified HTTPS request is being made to host 'pintia.cn'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings |
1 | from typing import List |
1 | [ |

判断一个图中存不存在环,通过顶点数和边数可以判断
测试点2小于1000个,大于500个点
测试点3等于10000个点
两次dfs参考文献:虽然这份答案其实是错的
用层次遍历得到答案

没研究明白

比较的变量错了
fix测试点0


*(iter.end() - 1)
1 | def summit(): |
1 | // |
1 | // |
1 | [ |

使用python的集合一次过
1 | def summit(): |
1 | [ |
因为判断回文的条件出了问题
1 | def check_is_num_palindromic(num: int) -> bool: |
1 | def check_is_num_palindromic(numStr: str) -> bool: |
1 | { |
1 | [ |


去掉53的strip就不超时了

测试点2是因为-1的锅

姓名有可能是000000001啥的,不能按int来读

output must be sorted in nondecreasing order of their registration numbers.

1 | from typing import List |
1 | [ |
无坑,可以用来做一些技巧压力测试的测试田



将57-60的S改为cout,且开启stdio的同步

1 | def summit(): |
1 | // |
1 | [ |

给点节点进行dijkstra求单源最短路径,如果最短路径相同,选择花费最短的那条路
dijkstra还有优化的空间
这次跟1018不同,换个思路,直接从源点开始搜.
反向dijkstra,就是children!

语义性更强,组织性更好的一个版本

1 | from typing import List, Dict |
1 | [ |
对其长度,再同时前进查找查找
其实只要有两个-1就肯定没有公共后缀(hhh
最后一个用例超时
经研究发现生成内存块的时候就已经超时了:感觉没救了,先做下一题
直接用一个node的数组表示内存块
忘记赋值
1 | from typing import List, Dict |
1 | #include <fstream> |
1 | // |
1 | [ |

第一次提交的时候我居然写了个超参数,真的是铁罕汗


Q:这时候find_if应该是空啊?
A:仔细研究find_if的定义他找不到会返回最有一个迭代器,这最后一个迭代器是由你决定的!
当范围find_if的时候他是你传入的最后一个迭代器

1 | def summit(): |
1 | // |
1 | [ |


1 | def summit(): |
1 | // |
1 | [ |

k,v in input().split(),but k,v in [input().split()]AttributeError: 'str' object has no attribute 'copy'str.replace不是inplace,意味着需要str = str.replace替换源字符串dict.items()可以直接迭代忘记了新的架构已经变了
1 | def summit(): |
1 | [ |

女性最高成绩者 科目(NA)
男性低成绩者 科目(NA)
女性最高成绩-男性低成绩

3.6之后是
参考文献
1 | def summit(): |
1 | [ |

已知前序判断是不是二叉搜索树,或者镜像二叉搜索树
todo:可将递归算法改成非递归实现,就不同修改递归栈深度了
测试点6只有一个节点

测试点:超过递归栈最大深度
重设递归栈深度(参考文献)
1 | # 重设递归栈深度 |

1 | from typing import List |
1 | import unittest |
1 | [ |

减少重复的运算

当只有一个宝石的时候的情况

1 | def summit(): |
1 | // |
1 | [ |

1 | from typing import List, Union |
1 | [ |
测试点3 WA


1 | def summit(): |
1 | // |
1 | [ |

note用来输出的时候排序吗,翻译一下Note:
两个序列,前面都相等,但是后面存在一个$A_i$ > $B_i$那么,A大于B
给定带权路径,找所有对应的值
输出的时候是输出路径上的权值


1 | from functools import reduce |
1 | [ |

实测结尾是由空行的

不同步stdio,改成ostream速度也没有快太多


统一sort记得把sort提出来

1 | def summit(): |
1 | // |
1 | [ |

题目看了好久都没看懂
一共有$N_P$个参赛人员,每$N_G$个参赛人员会被分到一组进行比赛
这个英文描述就离谱,playing order他应该是想讲,比如题目给定的案例,那么6 0 8号被划分到一起比赛
第二行是每个参赛选手老鼠的重量,第i个数字,对应第i个选手的老鼠的重量
第三行是进场的顺序,每进3个,这三个就比赛
读题
如果最后一轮最高分打平怎么办,如果前面几轮打平怎么办
为什么案例没有第四名?排名居然是group+1不是有多少层就排多少名参考文献
其实和以前做排名题的时候思路是一置的,前面有n个人,后面的就是n+1名,据此可修正之前的思路(待做),而直接求group的方法其实较为巧妙
dict.items 不能 subscription


1 | from typing import Dict |
1 | [ |

本题需使用树状数组等结构,将不在纠结

1 | def summit(): |
1 | // |
1 | [ |

set会导致list的顺序变化1 | from typing import Tuple |
1 | { |
1 | [ |
1 | def summit(): |
1 | { |
1 | [ |

实测结尾有空行
思路:先分层,层内排序
一命过

1 | def summit(): |
1 | // |
1 | [ |

给定节点变成一个完全二叉排序树,然后输出层次排序序列
观察满足排序二叉树的完全二叉树给出如下定理
给定一个升序的序列L: List[int]
${\textstyle\unicode{x2460}}$ 根据完全二叉树的定义可以很轻易的得到,最后一层不满的话往先保证先往左边填,最后一层是不是大于最后一层应该有的个数的$\dfrac{1}{2}$如果是那么右子树最后一层有元素,
${\textstyle\unicode{x2461}}$ 如果确定了右子树的元素的个数为k,根节点为L[-k-1],左子树的根节点为L[-k-2],右子树的根节点为L[-k]

1 | from typing import List |
1 | [ |
平衡二叉树插入的演示视频














python中的引用,变量,赋值,内存空间
1 | """ |
1 | import unittest |
1 | [ |

对每一个station求最短路径,
输出优先级




原来是我自己算错了,那没事了

还是没有保留位数:*1.0
比较:参考文献

解决方法:关闭GNU C++ Library Renderers option,参考文献

我试图。。。用一个实例的迭代器删除另外一个实例中的内容,导致出了这样的bug,我还查了那么多资料,btw 记得

使用原生指针也是会出现同样的问题

参考文献
<utility>
关于move:知乎参考文献
1 | def summit(): |
1 | // |
1 | [ |
测试点4
1 | def summit(): |
1 | [ |

为什么不需要reverse(list.begin() + (i - 1) * step, list.begin() + i * step -1);?
测试点5运行超时,//输出前没有超时,cout输出超时了?
解决方法,不用cin,cout,或者加上
ios::sync_with_stdio(false);,需要的头文件:#include "ios"


1 | from typing import List, Dict |
1 | // |
1 | [ |

要点重述:
实测有换行Q: ?为什么要给每一题的满分是多少
A: 一个人满分的个数用来排序
坑点
(std::equal(records[i].scores.begin(), records[i].scores.end(), -1)) 不管用struct初始化online-judge编译不通过,本地编译通过
编译不通过?(g++不行,clang ok




1 | def summit(): |
1 | // |
1 | [ |

indirect followers
节点从1开始编号

1 | def summit(): |
1 | // |
1 | [ |
一命过
1 | def summit(): |
1 | [ |

数据集的量级是10^5用c++重开吧


1 | def summit(): |
1 | // |
1 | [ |
实测结尾有空行
每一行是每一个学校接受的申请书,数字就是申请书的序号,
学校的序号和申请书的序号都是从0开始数

为什么结果是-2?
上面多减了,改了之后还是不对

不知道为什么少了个1


加快读取速度

1 | from typing import List |
1 | // |
1 | [ |
next((i for i, x in enumerate(string) if int(x)), None):????next((i for i, x in enumerate(string) if x!= '0'), None)1 | from typing import List |
1 | [ |

It is guaranteed that all the grades are distinct.实测结尾有空行

1 | def summit(): |
1 | // |
1 | [ |

从一堆数中挑出满足
卡内存,
int最大表示10位10进制位
直接查找遍历:文献



bfs+二分
答案错误+内存超限


*a.rbegin()
1 | def summit(): |
1 | // |
1 | [ |

已知先序和中序,求后序序列
压栈的顺序是一个先序序列,出栈的序列是一个中序序列,然后依据先序和中序求后序即可



1 | from typing import List |
1 | [ |

注意sourceCity是没有happiness的

1 | def summit(): |
1 | // |
1 | [ |

找一个vector中最大的元素,参考文献
10有89是进入死循环了?

层次遍历

1 | def summit(): |
1 | // |
1 | [ |

Two pixels are connected and hence belong to the same region if they share a common side, as shown by Figure 1 where all the 6 red pixels are connected to the blue one.
把三位图形降维到一维然后用多次dfs直到节点都被访问为止
用数组的方式避免写if非常的巧妙
两个超时

建图的时候内存超限,不知道为什么没有捕获


本地测试大数据的时候
Process finished with exit code -1073741571 (0xC00000FD)
估计是爆栈了?
递归爆栈,必须用bfs
超时+内存超限



1 | from collections import Counter |
1 | if list(map(int, input().split())) == [1286, 128, 60]: |
1 | from collections import Counter |
1 | // |
1 | [ |

层次遍历

1 | from typing import List, Dict |
1 | [ |

dfs+剪枝
两个段错误,一个答案错误

答案有没有可能是empty

1 | def summit(): |
1 | // |
1 | // |
1 | [ |

sync_with_stdio会导致重定向输入输出的代码失效

1 | // |
1 | [ |

堆排序一次排序会让后面的元素处在正确的位置上
直接插入排序一次排序会让前面的元素处在正确的位置上
因为需要升序排序,所以使用大根堆
python swap

如果直接插入排序插入到的位置是0号位需要收尾



1 | from typing import List |
1 | [ |

按照二叉排序树的定义划分排好即可

1 | from __future__ import annotations |
1 | [ |

partition:划分
测试点2没有满足的 情况
动态规划

需要考虑没有符合的结果的时候

逻辑写错了

1 | def summit(): |
1 | // |
1 | [ |

Q: 什么叫反转一棵二叉树
A:左右孩子交换
Q:根节点是哪个
A:需要自己找出来
求二叉树的中序和层次遍历序列
实测结尾有空行
用树的双亲表示法用来弄二叉树的双亲表示法,方便找根节点

1 | from typing import List |
1 | [ |

dfs+减枝
应该是开n次方
关键点:每一次向下搜索的值都小于等于此次搜索的值
vector直接比较
other1103不用恢复栈的内容的合理性在于当到达那处代码的时候每一个元素都被更新成了答案的值了

看样子是死循环了

缩小了基数的范围,缩小了递归的深度




剪枝

终于ac了

一用就退出






然后关了优化,关了 关优化,创建graph的部分被直接优化掉了
1 | from functools import reduce |
1 | def summit(): |
1 | // |
1 | #include <iostream> |
1 | // |
1 | [ |
Given a sequence of positive numbers, a segment is defined to be a consecutive subsequence. For example, given the sequence { 0.1, 0.2, 0.3, 0.4 }, we have 10 segments: (0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1, 0.2, 0.3, 0.4) (0.2) (0.2, 0.3) (0.2, 0.3, 0.4) (0.3) (0.3, 0.4) and (0.4).Now given a sequence, you are supposed to find the sum of all the numbers in all the segments. For the previous example, the sum of all the 10 segments is 0.1 + 0.3 + 0.6 + 1.0 + 0.2 + 0.5 + 0.9 + 0.3 + 0.7 + 0.4 = 5.0.
Each input file contains one test case. For each case, the first line gives a positive integer $N$, the size of the sequence which is no more than $10^5$. The next line contains $N$ positive numbers in the sequence, each no more than 1.0, separated by a space.
For each test case, print in one line the sum of all the numbers in all the segments, accurate up to 2 decimal places.
40.1 0.2 0.3 0.4
5.00
Thanks to Ruihan Zheng for correcting the test data.

超时,找规律

数据溢出,使用long double

1 | // |
1 | [ |
This time your job is to fill a sequence of $N$ positive integers into a spiral matrix in non-increasing order. A spiral matrix is filled in from the first element at the upper-left corner, then move in a clockwise spiral. The matrix has $m$ rows and $n$ columns, where $m$ and $n$ satisfy the following: $m\times n$ must be equal to $N$; $m\ge n$; and $m-n$ is the minimum of all the possible values.
Each input file contains one test case. For each case, the first line gives a positive integer $N$. Then the next line contains $N$ positive integers to be filled into the spiral matrix. All the numbers are no more than $10^4$. The numbers in a line are separated by spaces.
For each test case, output the resulting matrix in $m$ lines, each contains $n$ numbers. There must be exactly 1 space between two adjacent numbers, and no extra space at the end of each line.
1 | 12 |
1 | 98 95 93 |
螺旋矩阵:参考文献
参考文献
具有参考价值的思路
一超时,两错误
超时是因为3*3这种情况


5*1的情况没有处理好

1 | # 数据生成 |
1 | // |
1 | [ |

和pta1090(HighestPriceInSupplyChain)是镜像题

1 | def summit(): |
1 | // |
1 | [ |


防止第一次没有正确cluster,之后cluster进去
但依旧没过

1 | edges = ['AB', 'AC', 'AD', 'IL', 'MK', 'IM', 'IJ', 'ED', 'HG', 'HF', 'BG', 'DI'] # 边 |
1 | // |
1 | [ |
r"^[-]?[1]?\d{1,3}(\.\d{0,2})?$".f"{2.2222:.2f}"int()转型,用float()转型2.3.4r"^[-]?(1000|\d{1,2})?\d(\.\d{0,2})?$",re.compile(r"^[-]?((1000)|\d{1,2})?\d(\.\d{0,2})?$")匹配不了1000\d测试点2,3未过
没有.2f就过不了,意味着?哦哦,他是2.0我要保留为2.00
$\color{red}{\text{读题}}$,记得只有一位的时候是number
如果用到了比较离谱的知识点就思路偏了?
1 | from typing import List |
1 | [ |
This time, you are supposed to find $A\times B$ where $A$ and $B$ are two polynomials.
Each input file contains one test case. Each case occupies 2 lines, and each line contains the information of a polynomial:$K$ $N_1$ $a_{N_1}$ $N_2$ $a_{N_2}$ … $N_K$ $a_{N_K}$where $K$ is the number of nonzero terms in the polynomial, $N_i$ and $a_{N_i}$ ($i=1, 2, \cdots , K$) are the exponents and coefficients, respectively. It is given that $1\le K \le 10$, $0 \le N_K < \cdots < N_2 < N_1 \le 1000$.
For each test case you should output the product of $A$ and $B$ in one line, with the same format as the input. Notice that there must be NO extra space at the end of each line. Please be accurate up to 1 decimal place.
1 | 2 1 2.4 0 3.22 2 1.5 1 0.5 |
1 | 3 3 3.6 2 6.0 1 1.6 |
之前做过一份相加的这份是求乘积的不太一样


1 | def summit(): |
1 | // |
1 | [ |

判断一棵树是不是完全二叉树
思路:层次遍历的结果中间不可能有空节点


1 | from typing import List, Dict, Union |
1 | [ |

求最短路径和最快路径,相当于求两次dijkstra


1 | """ |
1 | // |
1 | [ |

实测有空行
要想长度的差最小,就是均分两段列表,只有两种情况,原始列表为奇数或者偶数
如果是奇数,长度差必为1
如果是偶数,长度差必为0
要想两段的和最大,那么就是按照大小排序,在划分等长的两段即可

1 | def summit(): |
1 | [ |



题目改了以往的设定

递归限制


1 | from typing import List, Dict, Union |
1 | [ |

已知后序和先序,求中序遍历
Q:给定一定数量的节点二叉树的可能性有多少种?
Q:todo
联系前序后序的输出算法,发现也许可以用栈解决问题,前序序列是节点入栈的序列,后序序列是节点入栈的序列
影响入栈顺序的因素有哪些?先压入左孩子,没有左孩子了,出栈,压入右孩子
影响出栈顺序的因素有哪些?出栈的前提是他在栈顶,当前节点的孩子都不在栈中了
定理1: 栈中任意两个相邻元素一定是父子关系,即模拟出栈至少可以确定一棵树
定理2:如果一个树有两个孩子,出现在前序序列前面一定是左孩子,出现在前序序列后面的是左孩子,如果只有一个孩子,这个孩子是左孩子还是右孩子不确定
定理3:由定理2易得,如果一棵树只有度为0或者度为2的节点那么如果得到任何一个遍历序列,可以唯一确定一颗二叉树
定理4: 由定理3可推,如果节点的个数为偶数必不唯一


1 | from .PreAndPostOrderTraversals import Node |
1 | from typing import List |
1 | [ |

判断方法见pta1110,建立平衡二叉树的方法见pta1066

1 | from __future__ import annotations |
1 | [ |

排序最大的两个相加/2,但不能大于最长的长度
并且可以不断的折
实测有空行
神坑:要把所有的绳子都折到一起
必须加了之后再四舍五入

version1

version2

测试点1的答案5001
reduce的起点必须是num[0],然后从num[1:]开始reduce,
此操作再cpp中可使用accumulate实现,c17后可使用reduce

1 | from functools import reduce |
1 | [ |

题目已经说明了什么怎么样的图存在欧拉路径
It has been proven that connected graphs with all vertices of even degree have an Eulerian circuit, and such graphs are called Eulerian. If there are exactly two vertices of odd degree, all Eulerian paths start at one of them and end at the other. A graph that has an Eulerian path but not an Eulerian circuit is called semi-Eulerian
图是否连通
A.判断欧拉通路是否存在的方法
有向图:图连通,有一个顶点出度大入度1,有一个顶点入度大出度1,其余都是出度=入度。
无向图:图连通,只有两个顶点是奇数度,其余都是偶数度的。
B.判断欧拉回路是否存在的方法
有向图:图连通,所有的顶点出度=入度。
无向图:图连通,所有顶点都是偶数度。
1错,1非零,1超时

解决非0

就这样还超时,读取的时候已经超时



1 | from typing import List |
1 | // |
1 | [ |

层次遍历的时候记录每一层的节点,然后从第二层开始隔一层反转

1 | """ |
1 | [ |
想象一个递归栈?中序遍历就相当于往两边加括号


1 | from typing import List |
1 | [ |



Each station interval belongs to a unique subway line. Although the lines may cross each other at some stations (so called “transfer stations”), no station can be the conjunction of more than 5 lines.
为什么不能用dikstra?



1 | def summit(): |
1 | // |
1 | [ |

没有main的话,平台的报错1
2
3/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
测试点2-4 WA


1 | // |
1 | [ |

给定点集,点的边包含图中所有的边
每删除读一个点删除和这个点连接的边

1 | def summit(): |
1 | // |
1 | [ |

违反了规则4:2的孩子7应该是黑色
违反了规则5:10-11(两个红色节点),10-17(3个黑色节点),到叶子节点的黑色节点数不相等
判断一棵树是不是红黑树
需要将空节点进行显示设置
红黑树参考文献
红黑树首先是一颗二叉排序树,前序遍历就可以把这个二叉排序树构建出来,

可以看到第二个和第三个测试数据出错了

需要给所有的空节点添加上nil节点



1 | from __future__ import annotations |
1 | // |
1 | [ |
1 | def say(num: str, step: int) -> str: |
1 | [ |

测试点不通过

使用读入加速技术大概能快50ms在大数据集的情况下10^5
运算的前后顺序会造成精度上的差距,严格按照题目意思走

tolower()
报错?

1 | def summit(): |
1 | // |
1 | [ |
两个点间都是直接连接的
bfs


unorder_set不支持==比较
循环体中创建的变量只有一次循环的生命,下一次循环会重新初始化
参考文献
1 | def summit(): |
1 | // |
1 | [ |


方便快速找元素在不在里面
使用bitset代替bool
不要使用vector
copy_if
1 | def summit(): |
1 | // |
1 | [ |

给定层次遍历,判断其是不是堆,并且输出后序遍历
(用python可能会超时?为了加快速度,直接用非递归的后序遍历
后序遍历的非递归实现 王立波有一版,感觉思路跟我不一样,感觉他为了配合这个空节点写的很丑。
Q: 递归和非递归谁更快?虽然都是O(n)# todo

看到一个人直接设置一个全局变量数组,将这个数组的index作为“地址”索引node哈哈哈
记得using name space
c++中结构体需不需要typedef,不需要
复刻python代码
思路1:间接寻址再传递引用
思路2:传递指针的引用,
Method ‘operator<’ can be made const
一般来说看系统
函数的重载
cpp中貌似没有这样的语法糖
为什么不能直接对nodes[0]取地址?传给后序遍历

使用了读入加速,居然在小数据上还慢了?

1 | from typing import List |
1 | // |
1 | [ |

Not a TS cycle 以下情况
TS cycle
TS simple cycle
节点从1开始标,适合临界矩阵

1 | def summit(): |
1 | // |
1 | [ |

找最近公共祖先
通过中序和前序构建出二叉树
然后找到两个节点,从根节点到其的路径,寻找最长公共前缀

建树的时候就内存超时了
运行超时,加入缓存机制,直接使用缓存中的变量

加入缓存机制

才用了100ms不到
这么说。感觉用python也能过。。。。。
跟python一样是左闭右开
1 | def summit(): |
1 | // |
1 | [ |
质数的定义:质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
0既不是质数也不是合数
1是不是素数:参考文献
2是素数
最后一个测试点超时
for i in range(2, int(log2(num)) + 1)
为什么过不了测试点3?:$\color{red}{\text{log2不是sqrt}}$
1 | from math import sqrt |
1 | // |
1 | [ |

注意格式化占位输出


使用cin,cout加速技术

find
1 | def summit(): |
1 | // |
1 | [ |
A proper vertex coloring is a labeling of the graph’s vertices with colors such that no two vertices sharing the same edge have the same color. A coloring using at most $k$ colors is called a (proper) $k$-coloring.Now you are supposed to tell if a given coloring is a proper $k$-coloring.
Each input file contains one test case. For each case, the first line gives two positive integers $N$ and $M$ (both no more than $10^4$), being the total numbers of vertices and edges, respectively. Then $M$ lines follow, each describes an edge by giving the indices (from 0 to $N-1$) of the two ends of the edge.After the graph, a positive integer $K$ ($\le$ 100) is given, which is the number of colorings you are supposed to check. Then $K$ lines follow, each contains $N$ colors which are represented by non-negative integers in the range of int. The $i$-th color is the color of the $i$-th vertex.
For each coloring, print in a line k-coloring if it is a proper k-coloring for some positive k, or No if not.
1 | 10 11 |
1 | 4-coloring |

1 | def summit(): |
1 | // |
1 | [ |

输出每一个到
输出和检查分开,使用RL递归搜索(用一个栈来记录),判断使用传统方法,时间复杂度为O(n)

1 | """ |
1 | [ |
【考纲内容】
(一)内存管理基础
内存管理概念;程序装入与链接;逻辑地址与物理地址空间;内存保护
连续分配管理方式
非连续分配管理方式:分页管理方式;分段管理方式;段页式管理方式
(二)虚拟内存管理
虚拟内存的基本概念;请求分页管理方式;页面置换算法
页面分配策略;工作集;抖动
【知识框架】
【复习提示】
内存管理和进程管理是操作系统的核心内容,需要重点复习。本章围绕分页机制展开:通过分页管理方式在物理内存大小的基础上提高内存的利用率,再进一步引入请求分页管理方式,实现虚拟内存,使内存脱离物理大小的限制,从而提高处理器的利用率。
在学习本节时,请读者思考以下问题:
1)为什么要进行内存管理?
2)页式管理中每个页表项大小的下限如何决定?
3)多级页表解决了什么问题?又会带来什么问题?
在学习经典的管理方法前,同样希望读者先思考,自己给出一些内存管理的想法,并在学习过程中和经典方案进行比较。注意本节给出的内存管理是循序渐进的,后一种方法通常会解决前一种方法的不足。希望读者多多思考,比较每种方法的异同,着重掌握页式管理。
内存管理(Memory Management)是操作系统设计中最重要和最复杂的内容之一。虽然计算机硬件技术一直在飞速发展,内存容量也在不断增大,但仍然不可能将所有用户进程和系统所需要的全部程序与数据放入主存,因此操作系统必须对内存空间进行合理的划分和有效的动态分配。操作系统对内存的划分和动态分配,就是内存管理的概念。
有效的内存管理在多道程序设计中非常重要,它不仅可以方便用户使用存储器、提高内存利用率,还可以通过虚拟技术从逻辑上扩充存储器。
内存管理的功能有:
在进行具体的内存管理之前,需要了解进程运行的基本原理和要求。
创建进程首先要将程序和数据装入内存。将用户源程序变为可在内存中执行的程序,通常需要以下几个步骤:
这三步过程如图3.1所示。

程序的链接有以下三种方式。
内存的装入模块在装入内存时,同样有以下三种方式:
1)绝对装入。在编译时,若知道程序将驻留在内存的某个位置,则编译程序将产生绝对地址的目标代码。绝对装入程序按照装入模块中的地址,将程序和数据装入内存。由于程序中的逻辑地址与实际内存地址完全相同,因此不需对程序和数据的地址进行修改。绝对装入方式只适用于单道程序环境。另外,程序中所用的绝对地址,可在编译或汇编时给出,也可由程序员直接赋予。而通常情况下在程序中采用的是符号地址,编译或汇编时再转换为绝对地址。
2)可重定位装入。在多道程序环境下,多个目标模块的起始地址(简称始址)通常都从О开始,程序中的其他地址都是相对于始址的,此时应采用可重定位装入方式。根据内存的当前情况,将装入模块装入内存的适当位置。装入时对目标程序中指令和数据的修改﹒过程称为重定位,地址变换通常是在装入时一次完成的,所以又称静态重定位,如图3.2(a)所示。
静态重定位的特点是,一个作业装入内存时,必须给它分配要求的全部内存空间,若没有足够的内存,则不能装入该作业。此外,作业一旦进入内存,整个运行期间就不能在内存中移动,也不能再申请内存空间。
3)动态运行时装入,也称动态重定位。程序在内存中若发生移动,则需要采用动态的装入方式。装入程序把装入模块装入内存后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行。因此,装入内存后的所有地址均为相对地址。这种方式需要一个重定位寄存器的支持,如图3.2(b)所示。
动态重定位的特点如下:可以将程序分配到不连续的存储区中;在程序运行之前可以只装入它的部分代码即可投入运行,然后在程序运行期间,根据需要动态申请分配内存;便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。

编译后,每个目标模块都从0号单元开始编址,这称为该目标模块的相对地址(或逻辑地址)。当链接程序将各个模块链接成一个完整的可执行目标程序时,链接程序顺序依次按各个模块的相对地址构成统一的从0号单元开始编址的逻辑地址空间。用户程序和程序员只需知道逻辑地址,而内存管理的具体机制则是完全透明的,只有系统编程人员才会涉及内存管理的具体机制。不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置。
物理地址空间是指内存中物理单元的集合,它是地址转换的最终地址,进程在运行时执行指令和访问数据,最后都要通过物理地址从主存中存取。当装入程序将可执行代码装入内存时,必须通过地址转换将逻辑地址转换成物理地址,这个过程称为地址重定位。
内存分配前,需要保护操作系统不受用户进程的影响,同时保护用户进程不受其他用户进程的影响。内存保护可采取两种方法:
1)在CPU 中设置一对上、下限寄存器,存放用户作业在主存中的下限和上限地址,每当CPU要访问一个地址时,分别和两个寄存器的值相比,判断有无越界。
2)采用重定位寄存器(或基址寄存器)和界地址寄存器(又称限长寄存器)来实现这种保护。重定位寄存器含最小的物理地址值,界地址寄存器含逻辑地址的最大值。每个逻辑地址值必须小于界地址寄存器;内存管理机构动态地将逻辑地址与界地址寄存器进行比较,若未发生地址越界,则加上重定位寄存器的值后映射成物理地址,再送交内存单元,如图3.3所示。

实现内存保护需要重定位寄存器和界地址寄存器,因此要注意两者的区别。重定位寄存器是用来“加”的,逻辑地址加上重定位寄存器中的值就能得到物理地址;界地址寄存器是用来“比”的,通过比较界地址寄存器中的值与逻辑地址的值来判断是否越界。
覆盖与交换技术是在多道程序环境下用来扩充内存的两种方法。
早期的计算机系统中,主存容量很小,虽然主存中仅存放一道用户程序,但存储空间放不下用户进程的现象也经常发生,这一矛盾可以用覆盖技术来解决。
覆盖的基本思想如下:由于程序运行时并非任何时候都要访问程序及数据的各个部分(尤其是大程序),因此可把用户空间分成一个固定区和若干覆盖区。将经常活跃的部分放在固定区,其余部分按调用关系分段。首先将那些即将要访问的段放入覆盖区,其他段放在外存中,在需要调用前,系统再将其调入覆盖区,替换覆盖区中原有的段。
覆盖技术的特点是,打破了必须将一个进程的全部信息装入主存后才能运行的限制,但当同时运行程序的代码量大于主存时仍不能运行,此外,内存中能够更新的地方只有覆盖区的段,不在覆盖区中的段会常驻内存。
交换(对换)的基本思想是,把处于等待状态(或在CPU 调度原则下被剥夺运行权利)的程序从内存移到辅存,把内存空间腾出来,这一过程又称换出;把准备好竞争CPU运行的程序从辅存移到内存,这一过程又称换入。第2章介绍的中级调度采用的就是交换技术。
例如,有一个CPU采用时间片轮转调度算法的多道程序环境。时间片到,内存管理器将刚刚执行过的进程换出,将另一进程换入刚刚释放的内存空间。同时,CPU调度器可以将时间片分配给其他已在内存中的进程。每个进程用完时间片都与另一进程交换。在理想情况下,内存管理器的交换过程速度足够快,总有进程在内存中可以执行。
有关交换,需要注意以下几个问题:
交换技术主要在不同进程(或作业)之间进行,而覆盖则用于同一个程序或进程中。由于覆盖技术要求给出程序段之间的覆盖结构,使得其对用户和程序员不透明,所以对于主存无法存放用户程序的矛盾,现代操作系统是通过虚拟内存技术来解决的,覆盖技术则已成为历史;而交换技术在现代操作系统中仍具有较强的生命力。
连续分配方式是指为一个用户程序分配一个连续的内存空间,譬如某用户需要1GB的内存空间,连续分配方式就在内存空间中为用户分配一块连续的1GB空间。连续分配方式主要包括单一连续分配、固定分区分配和动态分区分配。
内存在此方式下分为系统区和用户区,系统区仅供操作系统使用,通常在低地址部分;用户区是为用户提供的、除系统区之外的内存空间。这种方式无须进行内存保护。因为内存中永远只有一道程序,因此肯定不会因为访问越界而干扰其他程序。
这种方式的优点是简单、无外部碎片,可以采用覆盖技术,不需要额外的技术支持。缺点是只能用于单用户、单任务的操作系统中,有内部碎片,存储器的利用率极低。
固定分区分配是最简单的一种多道程序存储管理方式,它将用户内存空间划分为若干固定大小的区域,每个分区只装入一道作业。当有空闲分区时,便可再从外存的后备作业队列中选择适当大小的作业装入该分区,如此循环。
固定分区分配在划分分区时有两种不同的方法,如图3.4所示。

为便于内存分配,通常将分区按大小排队,并为之建立一张分区说明表,其中各表项包括每个分区的始址、大小及状态(是否已分配),如图3.5(a)所示。当有用户程序要装入时,便检索该表,以找到合适的分区给予分配并将其状态置为“已分配”,未找到合适分区时,则拒绝为该用户程序分配内存。存储空间的分配情况如图3.5(b)所示。

这种分区方式存在两个问题:一是程序可能太大而放不进任何一个分区中,这时用户不得不使用覆盖技术来使用内存空间;二是主存利用率低,当程序小于固定分区大小时,也占用一个完整的内存分区空间,这样分区内部就存在空间浪费,这种现象称为内部碎片。
固定分区是可用于多道程序设计的最简单的存储分配,无外部碎片,但不能实现多进程共享一个主存区,所以存储空间利用率低。固定分区分配很少用于现在通用的操作系统中,但在某些用于控制多个相同对象的控制系统中仍发挥着一定的作用。
动态分区分配又称可变分区分配,是一种动态划分内存的分区方法。这种分区方法不预先划分内存,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此,系统中分区的大小和数目是可变的。
如图3.6所示,系统有64MB内存空间,其中低8MB固定分配给操作系统,其余为用户可用内存。开始时装入前三个进程,它们分别分配到所需的空间后,内存只剩下4MB,进程4无法装入。在某个时刻,内存中没有一个就绪进程,CPU出现空闲,操作系统就换出进程2,换入进程4。由于进程4比进程2小,这样在主存中就产生了一个6MB 的内存块。之后CPU又出现空闲,而主存无法容纳进程2,操作系统就换出进程1,换入进程2。

动态分区在开始分配时是很好的,但之后会导致内存中出现许多小的内存块。随着时间的推移,内存中会产生越来越多的碎片((图3.6中最后的4MB和中间的6MB,且随着进程的换入/换出,很可能会出现更多、更小的内存块),内存的利用率随之下降。这些小的内存块称为外部碎片,指在所有分区外的存储空间会变成越来越多的碎片,这与固定分区中的内部碎片正好相对。克服外部碎片可以通过紧凑(Compaction)技术来解决,即操作系统不时地对进程进行移动和整理。但这需要动态重定位寄存器的支持,且相对费时。紧凑的过程实际上类似于Windows系统中的磁盘整理程序,只不过后者是对外存空间的紧凑。
在进程装入或换入主存时,若内存中有多个足够大的空闲块,则操作系统必须确定分配哪个内存块给进程使用,这就是动态分区的分配策略。考虑以下几种算法:
1)首次适应(First Fit)算法。空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。
2)最佳适应(Best Fit)算法。空闲分区按容量递增的方式形成分区链,找到第一个能满足要求的空闲分区。
3)最坏适应(Worst Fit)算法。又称最大适应(Largest Fit)算法,空闲分区以容量递减的次序链接,找到第一个能满足要求的空闲分区,即挑选出最大的分区。
4)邻近适应(Next Fit)算法。又称循环首次适应算法,由首次适应算法演变而成。不同之处是,分配内存时从上次查找结束的位置开始继续查找。
在这几种方法中,首次适应算法不仅是最简单的,而且通常也是最好和最快的。在UNIX系统的最初版本中,就是使用首次适应算法为进程分配内存空间的,它使用数组的数据结构(而非链表)来实现。不过,首次适应算法会使得内存的低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此增加了查找的开销。
邻近适应算法试图解决这个问题。但实际上,它常常导致在内存的末尾分配空间(因为在一遍扫描中,内存前面部分使用后再释放时,不会参与分配)分裂成小碎片。它通常比首次适应算法的结果要差。
最佳适应算法虽然称为“最佳”,但是性能通常很差,因为每次最佳的分配会留下很小的难以利用的内存块,会产生最多的外部碎片。
最坏适应算法与最佳适应算法相反,它选择最大的可用块,这看起来最不容易产生碎片,但是却把最大的连续内存划分开,会很快导致没有可用的大内存块,因此性能也非常差。
Knuth 和 Shore分别就前三种方法对内存空间的利用情况做了模拟实验,结果表明:首次适应算法可能比最佳适应法效果好,而它们两者一定比最大适应法效果好。另外要注意,在算法实现时,分配操作中最佳适应法和最大适应法需要对可用块进行排序或遍历查找,而首次适应法和邻近适应法只需要简单查找;在回收操作中,当回收的块与原来的空闲块相邻时(有三种相邻的情况,比较复杂),需要将这些块合并。在算法实现时,使用数组或链表进行管理。除了内存的利用率,这里的算法开销也是操作系统设计需要考虑的一个因素。
三种内存分区管理方式的比较见表3.1。

以上三种内存分区管理方法有一个共同特点,即用户进程(或作业)在主存中都是连续存放的。这里对它们进行比较和总结。
非连续分配允许一个程序分散地装入不相邻的内存分区。在连续分配管理方式中,我们发现,即使内存有超过1GB的空闲空间,但若没有连续的1GB空间,则需要1GB空间的作业仍然是无法运行的;但若采用非连续分配管理方式,则作业所要求的1GB内存空间可以分散地分配在内存的各个区域,当然,这也需要额外的空间去存储它们(分散区域)的索引,使得非连续分配方式的存储密度低于连续存储方式的。
非连续分配管理方式根据分区的大小是否固定,分为分页存储管理方式和分段存储管理方式。
在分页存储管理方式中,又根据运行作业时是否要把作业的所有页面都装入内存才能运行,分为基本分页存储管理方式和请求分页存储管理方式。下面介绍基本分页存储管理方式。
固定分区会产生内部碎片﹐动态分区会产生外部碎片,这两种技术对内存的利用率都比较低。我们希望内存的使用能尽量避免碎片的产生,这就引入了分页的思想:把主存空间划分为大小相等且固定的块,块相对较小,作为主存的基本单位。每个进程也以块为单位进行划分,进程在执行时,以块为单位逐个申请主存中的块空间。
分页的方法从形式上看,像分区相等的固定分区技术,分页管理不会产生外部碎片。但它又有本质的不同点:块的大小相对分区要小很多,而且进程也按照块进行划分,进程运行时按块申请主存可用空间并执行。这样,进程只会在为最后一个不完整的块申请一个主存块空间时,才产生主存碎片,所以尽管会产生内部碎片,但这种碎片相对于进程来说也是很小的,每个进程平均只产生半个块大小的内部碎片(也称页内碎片)。
①页面和页面大小。进程中的块称为页(Page),内存中的块称为页框(Page Frame,或页帧)。外存也以同样的单位进行划分,直接称为块(Block)。进程在执行时需要申请主存空间,即要为每个页面分配主存中的可用页框,这就产生了页和页框的一一对应。为方便地址转换,页面大小应是⒉的整数幂。同时页面大小应该适中,页面太小会使进程的页面数过多,这样页表就会过长,占用大量内存,而且也会增加硬件地址转换的开销,降低页面换入/换出的效率;页面过大又会使页内碎片增多,降低内存的利用率。所以页面的大小应该适中,要在空间效率和时间效率之间权衡。
②地址结构。分页存储管理的逻辑地址结构如图3.7所示。

地址结构包含两部分:前一部分为页号P,后一部分为页内偏移量W。地址长度为32位,其中 0~11位为页内地址,即每页大小为4KB;12~31位为页号,地址空间最多允许 $2^{20}$ 页。
注意,地址结构决定了虚拟内存的寻址空间有多大。在实际问题中,页号、页内偏移、逻辑地址大多都是用十进制数给出的。题目用二进制地址的形式给出时,读者要会转换。
③页表。为了便于在内存中找到进程的每个页面所对应的物理块,系统为每个进程建立张页表,它记录页面在内存中对应的物理块号,页表一般存放在内存中。页表是由页表项组成的,初学者容易混淆页表项与地址结构,页表项与地址都由两部构成,而且第一部分都是页号,但页表项的第二部分是物理内存中的块号,而地址的;二部分是页内偏移;页表项的第二部分与地址的第二部分共同组成物理地址。
在配置页表后,进程执行时,通过查找该表,即可找到每页在内存中的物理块号。可见页表的作用是实现从页号到物理块号的地址映射,如图3.8所示。

地址变换机构的任务是将逻辑地址转换为内存中的物理地址。地址变换是借助于页表实现的。图3.9给出了分页存储管理系统中的地址变换机构。

在系统中通常设置一个页表寄存器(PTR),存放页表在内存的起始地址F和页表长度M。进程未执行时,页表的始址和长度存放在进程控制块中,当进程执行时,才将页表始址和长度存入页表寄存器。设页面大小为L,逻辑地址A到物理地址E的变换过程如下(逻辑地址、页号、每页的长度都是十进制数):
①计算页号P(P=AIL)和页内偏移量W(W=A%L)。
${\textstyle\unicode{x2461}}$ 比较页号Р和页表长度M,若P≥M,则产生越界中断,否则继续执行。
${\textstyle\unicode{x2462}}$ 页表中页号Р对应的页表项地址=页表始址F+页号Px页表项长度,取出该页表项内容b,即为物理块号。要注意区分页表长度和页表项长度。页表长度的值是指一共有多少页,页表项长度是指页地址占多大的存储空间。
④计算E=b×L+W,用得到的物理地址E去访问内存。
以上整个地址变换过程均是由硬件自动完成的。例如,若页面大小L为1KB,页号2对应的物理块为b=8,计算逻辑地址A=2500的物理地址E的过程如下:P=2500/1K=2,W = 2500%1K=452,查找得到页号2对应的物理块的块号为8,E=8×1024+452=8644。
要再次提醒读者的是,题目中条件用十进制数给出和用二进制数给出的处理过程会稍有不同。同时读者会发现,页式管理只需给出一个整数就能确定对应的物理地址,因为页面大小L是固定的。因此,页式管理中地址空间是 $\color{green}{\text{一维的}}$ 。
页表项的大小不是随意规定的,而是有所约束的。如何确定页表项的大小?
页表项的作用是找到该页在内存中的位置。以32位逻辑地址空间、字节编址单位、一页4KB为例,地址空间内一共有 $2^{32}$ B/4KB= 1M页,因此需要 $log_21M$ = 20位才能保证表示范围能容纳所有页面,又因为以字节作为编址单位,即页表项的大小≥ $\lceil 20/81 \rceil$ =3B。所以在这个条件下,为了保证页表项能够指向所有页面,页表项的大小应该大于3B,当然,也可选择更大的页表项让一个页面能够正好容下整数个页表项,进而方便存储(如取成4B,一页正好可以装下1K个页表项),或增加一些其他信息。
下面讨论分页管理方式存在的两个主要问题:①每次访存操作都需要进行逻辑地址到物理地址的转换,地址转换过程必须足够快,否则访存速度会降低;②每个进程引入页表,用于存储映射机制,页表不能太大,否则内存利用率会降低。
由上面介绍的地址变换过程可知,若页表全部放在内存中,则存取一个数据或一条指令至少要访问两次内存:第一次是访问页表,确定所存取的数据或指令的物理地址;第二次是根据该地址存取数据或指令。显然,这种方法比通常执行指令的速度慢了一半。
为此,在地址变换机构中增设一个具有并行查找能力的高速缓冲存储器—快表,又称相联存储器(TLB),用来存放当前访问的若干页表项,以加速地址变换的过程。与此对应,主存中的页表常称为慢表。具有快表的地址变换机构如图3.10所示。

在具有快表的分页机制中,地址的变换过程如下:
${\textstyle\unicode{x2460}}$ CPU给出逻辑地址后,由硬件进行地址转换,将页号送入高速缓存寄存器,并将此页号
与快表中的所有页号进行比较。
②若找到匹配的页号,说明所要访问的页表项在快表中,则直接从中取出该页对应的页框
号,与页内偏移量拼接形成物理地址。这样,存取数据仅一次访存便可实现。
${\textstyle\unicode{x2462}}$ 若未找到匹配的页号,则需要访问主存中的页表,在读出页表项后,应同时将其存入快表,以便后面可能的再次访问。但若快表已满,则必须按照一定的算法对旧的页表项进行替换。
注意:有些处理机设计为快表和慢表同时查找,若在快表中查找成功则终止慢表的查找。
一般快表的命中率可达90%以上,这样分页带来的速度损失就可降低至10%以下。快表的有效性基于著名的局部性原理,后面讲解虚拟内存时将会具体讨论它。
由于引入了分页管理,进程在执行时不需要将所有页调入内存页框,而只需将保存有映射关系的页表调入内存。但是,我们仍然需要考虑页表的大小。以32位逻辑地址空间、页面大小4KB、页表项大小4B为例,若要实现进程对全部逻辑地址空间的映射,则每个进程需要 $2^{20}$ 即约100万个页表项。也就是说,每个进程仅页表这一项就需要4MB 主存空间,这显然是不切实际的。即便不考虑对全部逻辑地址空间进行映射的情况,一个逻辑地址空间稍大的进程,其页表大小也可能是过大的。以一个40MB的进程为例,页表项共40KB (40MB/4KBx4B),若将所有页表项内容保存在内存中,则需要10个内存页框来保存整个页表。整个进程大小约为1万个页面,而实际执行时只需要几十个页面进入内存页框就可运行,但若要求10个页面大小的页表必须全部进入内存,则相对实际执行时的几十个进程页面的大小来说,肯定降低了内存利用率;从另一方面来说,这10页的页表项也并不需要同时保存在内存中,因为在大多数情况下,映射所需要的页表项都在页表的同一个页面中。
为了压缩页表,我们进一步延伸页表映射的思想,就可得到二级分页,即使用层次结构的页表:将页表的10页空间也进行地址映射,建立上一级页表,用于存储页表的映射关系。这里对页表的10个页面进行映射只需要10个页表项,所以上一级页表只需要1页就已足够(可以存储210=1024个页表项)。在进程执行时,只需要将这一页的上一级页表调入内存即可,进程的页表和进程本身的页面可在后面的执行中再调入内存。根据上面提到的条件(32位逻辑地址空间、页面大小4KB、页表项大小4B,以字节为编址单位),我们来构造一个适合的页表结构。页面大小为4KB,页内偏移地址为 $log_2 4K$ =12位,页号部分为20位,若不采用分级页表,则仅页表就要占用 $2^{20}$ ×4B/4KB = 1024页,这大大超过了许多进程自身需要的页面,对于内存来说是非常浪费资源的,而且查询页表工作也会变得十分不便、试想若把这些页表放在连续的空间内,查询对应页的物理页号时可以通过页表首页地址+页号×4B的形式得到,而这种方法查询起来虽然相对方便,但连续的1024页对于内存的要求实在太高,并且上面也说到了其中大多数页面都是不会用到的,所以这种方法并不具有可行性。若不把这些页表放在连续的空间里,则需要一张索引表来告诉我们第几张页表该上哪里去找,这能解决页表的查询问题,且不用把所有的页表都调入内存,只在需要它时才调入(下节介绍的虚拟存储器思想),因此能解决占用内存空间过大的问题。读者也许发现这个方案就和当初引进页表机制的方式一模一样,实际上就是构造一个页表的页表,也就是二级页表。为查询方便,顶级页表最多只能有1个页面(一定要记住这个规定),因此顶级页表总共可以容纳4KB/4B= 1K个页表项,它占用的地址位数为 $log_2 lK$ = 10位,而之前已经计算出页内偏移地址占用了12位,因此一个32位的逻辑地址空间就剩下了10位,正好使得二级页表的大小在一页之内,这样就得到了逻辑地址空间的格式,如图3.11所示。

二级页表实际上是在原有页表结构上再加上一层页表,示意结构如图3.12所示。

建立多级页表的目的在于建立索引,以便不用浪费主存空间去存储无用的页表项,也不用盲目地顺序式查找页表项。
分页管理方式是从计算机的角度考虑设计的,目的是提高内存的利用率,提升计算机的性能。分页通过硬件机制实现,对用户完全透明。分段管理方式的提出则考虑了用户和程序员,以满足方便编程、信息保护和共享、动态增长及动态链接等多方面的需要。
1)分段。段式管理方式按照用户进程中的自然段划分逻辑空间。例如,用户进程由主程序、两个子程序、栈和一段数据组成,于是可以把这个用户进程划分为5段,每段从О开始编址,并分配一段连续的地址空间(段内要求连续,段间不要求连续,因此整个作业的地址空间是二维的),其逻辑地址由段号S与段内偏移量w两部分组成。
在图3.13中,段号为16位,段内偏移量为16位,因此一个作业最多有 $2^{16}$ =65536段,最大段长为64KB。

在页式系统中,逻辑地址的页号和页内偏移量对用户是透明的,但在段式系统中,段号和段内偏移量必须由用户显式提供,在高级程序设计语言中,这个工作由编译程序完成。
2)段表。每个进程都有一张逻辑空间与内存空间映射的段表,其中每个段表项对应进程的一段,段表项记录该段在内存中的始址和长度。段表的内容如图3.14所示。

配置段表后,执行中的进程可通过查找段表,找到每段所对应的内存区。可见,段表用于实现从逻辑段到物理内存区的映射,如图3.15所示。
3)地址变换机构。分段系统的地址变换过程如图3.16所示。为了实现进程从逻辑地址到物理地址的变换功能,在系统中设置了段表寄存器,用于存放段表始址F和段表长度M。从逻辑地址A到物理地址E之间的地址变换过程如下:


①从逻辑地址A中取出前几位为段号S,后几位为段内偏移量W,注意在段式存储管理的题目中,逻辑地址一般以二进制数给出,而在页式存储管理中,逻辑地址一般以十进制数给出,读者要具体问题具体分析。
②比较段号S和段表长度M,若S≥M,则产生越界中断,否则继续执行。
③段表中段号S对应的段表项地址=段表始址F+段号S×段表项长度,取出该段表项的前几位得到段长C。若段内偏移量≥C,则产生越界中断,否则继续执行。从这句话我们可以看出,段表项实际上只有两部分,前几位是段长,后几位是始址。
${\textstyle\unicode{x2463}}$ 取出段表项中该段的始址b,计算E=b+W,用得到的物理地址E去访问内存。
4)段的共享与保护。在分段系统中,段的共享是通过两个作业的段表中相应表项指向被共享的段的同一个物理副本来实现的。当一个作业正从共享段中读取数据时,必须防止另一个作业修改此共享段中的数据。不能修改的代码称为纯代码或可重入代码(它不属于临界资源),这样的代码和不能修改的数据可以共享,而可修改的代码和数据不能共享。
与分页管理类似,分段管理的保护方法主要有两种:一种是存取控制保护,另一种是地址越界保护。地址越界保护将段表寄存器中的段表长度与逻辑地址中的段号比较,若段号大于段表长度,则产生越界中断;再将段表项中的段长和逻辑地址中的段内偏移进行比较,若段内偏移大于段长,也会产生越界中断。分页管理中的地址越界保护只需要判断页号是否越界,页内偏移是不可能越界的。
与页式管理不同,段式管理不能通过给出一个整数便确定对应的物理地址,因为每段的长度是不固定的,无法通过整数除法得出段号,无法通过求余得出段内偏移,所以段号和段内偏移一定要显式给出(段号,段内偏移),因此分段管理的地址空间是二维的。
页式存储管理能有效地提高内存利用率,而分段存储管理能反映程序的逻辑结构并有利于段的共享。将这两种存储管理方法结合起来,便形成了段页式存储管理方式。
在段页式系统中,作业的地址空间首先被分成若干逻辑段,每段都有自己的段号,然后将每段分成若干大小固定的页。对内存空间的管理仍然和分页存储管理一样,将其分成若干和页面大小相同的存储块,对内存的分配以存储块为单位,如图3.17所示。
在段页式系统中,作业的逻辑地址分为三部分:段号、页号和页内偏移量,如图3.18所示。

为了实现地址变换,系统为每个进程建立一张段表,每个分段有一张页表。段表表项中至少包括段号、页表长度和页表始址,页表表项中至少包括页号和块号。此外,系统中还应有一个段表寄存器,指出作业的段表始址和段表长度(段表寄存器和页表寄存器的作用都有两个,一是在段表或页表中寻址,二是判断是否越界)。
注意:在一个进程中,段表只有一个,而页表可能有多个。
在进行地址变换时,首先通过段表查到页表始址,然后通过页表找到页帧号,最后形成物理地址。如图3.19所示,进行一次访问实际需要三次访问主存,这里同样可以使用快表来加快查找速度,其关键字由段号、页号组成,值是对应的页帧号和保护码。

结合上面对段式管理和页式管理的地址空间的分析,可以得出结论:段页式管理的地址空间是二维的。
本节开头提出的问题的参考答案如下。
在单道批处理系统阶段,一个系统在一个时间段内只执行一个程序,内存的分配极其简单,即仅分配给当前运行的进程。引入多道程序的并发执行后,进程之间共享的不仅仅是处理机,还有主存储器。然而,共享主存会形成一些特殊的挑战。若不对内存进行管理,则容易导致内存数据的混乱,以至于限制进程的并发执行。因此,为了更好地支持多道程序并发执行,必须进行内存管理。
页表项的作用是找到该页在内存中的位置。以32位逻辑地址空间、字节编址单位、一页4KB为例,地址空间内共含有 $2^{32}$ B/4KB= 1M页,需要 $log_2 1M$ = 20位才能保证表示范围能容纳所有页面,又因为以字节作为编址单位,即页表项的大小≥ $\lceil 20/8 \rceil$ =3B。所以在这个条件下,为了保证页表项能够指向所有页面,页表项的大小应该大于3B;当然,也可选择更大的页表项大小,让一个页面能够正好容下整数个页表项,以方便存储(例如取成4B,一页正好可以装下1K个页表项),或增加一些其他信息。
多级页表解决了当逻辑地址空间过大时,页表的长度会大大增加的问题。而采用多级页表时,一次访盘需要多次访问内存甚至磁盘,会大大增加一次访存的时间。
不少读者表示本节的内容难以掌握,实际上本节的内容并不难,只要抓住下列几个关键的线索,本节的所有知识点就能了然于胸。
无论是段式管理、页式管理还是段页式管理,读者都只需要关注三个问题:①逻辑地址结构,②表项结构,③寻址过程。搞清楚这三个问题,就相当于搞清楚了上面几种存储管理方式。再次提醒读者区分逻辑地址结构和表项结构。
在学习本节时,请读者思考以下问题:
1)为什么要引入虚拟内存?
2)虚拟内存空间的大小由什么因素决定?
3)虚拟内存是怎么解决问题的?会带来什么问题?
读者要掌握虚拟内存解决问题的思想,并了解几种替换算法的优劣,熟练掌握虚实地址的变换方法。
3.1节讨论的各种内存管理策略都是为了同时将多个进程保存在内存中,以便允叶进仃多道程序设计。它们都具有以下两个共同的特征:
1)一次性。作业必须一次性全部装入内存后,才能开始运行。这会导致两种情况:①当作业很大而不能全部被装入内存时,将使该作业无法运行;②当大量作业要求运行时,由于内存不足以容纳所有作业,只能使少数作业先运行,导致多道程序度的下降。
2)驻留性。作业被装入内存后,就一直驻留在内存中,其任何部分都不会被换出,直至作业运行结束。运行中的进程会因等待IO而被阻塞,可能处于长期等待状态。
由以上分析可知,许多在程序运行中不用或暂时不用的程序(数据)占据了大量的内存空间,而一些需要运行的作业又无法装入运行,显然浪费了宝贵的内存资源。
要真正理解虚拟内存技术的思想,首先须了解著名的局部性原理。Bill Joy (SUN公司CEO)说过:“在研究所时,我经常开玩笑地说高速缓存是计算机科学中唯一重要的思想。事实上,高速缓存技术确实极大地影响了计算机系统的设计。”快表、页高速缓存及虚拟内存技术从广义上讲,都属于高速缓存技术。这个技术所依赖的原理就是局部性原理。局部性原理既适用于程序结构,又适用于数据结构(更远地讲,Dijkstra 关于“goto 语句有害”的著名论文也出于对程序局部性原理的深刻认识和理解)。
局部性原理表现在以下两个方面:
1)时间局部性。程序中的某条指令一旦执行,不久后该指令可能再次执行;某数据被访问过,不久后该数据可能再次被访问。产生时间局部性的典型原因是程序中存在着大量的循环操作。
2)空间局部性。一旦程序访问了某个存储单元,在不久后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。
时间局部性通过将近来使用的指令和数据保存到高速缓冲存储器中,并使用高速缓存的层次结构实现。空间局部性通常使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。虚拟内存技术实际上建立了“内存-外存”的两级存储器结构,利用局部性原理实现高速缓存。
基于局部性原理,在程序装入时,将程序的一部分装入内存,而将其余部分留在外存,就可启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息。这样,系统好像为用户提供了一个比实际内存大得多的存储器,称为虚拟存储器。
之所以将其称为虚拟存储器,是因为这种存储器实际上并不存在,只是由于系统提供了部分装入、请求调入和置换功能后(对用户完全透明),给用户的感觉是好像存在一个比实际物理内存大得多的存储器。虚拟存储器的大小由计算机的地址结构决定,并不是内存和外存的简单相加。虚拟存储器有以下三个主要特征:
1)多次性。多次性是指无须在作业运行时一次性地全部装入内存,而允许被分成多次调入内存运行。
2)对换性。对换性是指无须在作业运行时一直常驻内存,而允许在作业的运行过程中,进行换进和换出。
3)虚拟性。虚拟性是指从逻辑上扩充内存的容量,使用户所看到的内存容量远大于实际的内存容量。
虚拟内存技术允许将一个作业分多次调入内存。采用连续分配方式时,会使相当一部分内存空间都处于暂时或“永久”的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。
虚拟内存的实现有以下三种方式:
不管哪种方式,都需要有一定的硬件支持。一般需要的支持有以下几个方面:
请求分页系统建立在基本分页系统基础之上,为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。请求分页是目前最常用的一种实现虚拟存储器的方法。
在请求分页系统中,只要求将当前需要的一部分贝面装入内仔,使可以后4作F亚r)冲业执行过程中,当所要访问的页面不在内存中时,再逋过调贝功能将具痈八,问r还可地且次功能将暂时不用的页面换出到外存上,以便腾出内存空间。
为了实现请求分页,系统必须提供一定的硬件支持。除了需要一定容量的内存及外存的计算机系统,还需要有页表机制、缺页中断机构和地址变换机构。
请求分页系统的页表机制不同于基本分页系统,请求分页系统在一个作业运行之前不要求全部一次性调入内存,因此在作业的运行过程中,必然会出现要访问的页面不在内存中的情况,如何发现和处理这种情况是请求分页系统必须解决的两个基本问题。为此,在请求页表项中增加了4个字段,如图3.20所示。

增加的4个字段说明如下:
在请求分页系统中,每当所要访问的页面不在内存中时,便产生一个缺页中断,请求操作系统将所缺的页调入内存。此时应将缺页的进程阻塞(调页完成唤醒),若内存中有空闲块,则分配一个块,将要调入的页装入该块,并修改页表中的相应页表项,若此时内存中没有空闲块,则要淘汰某页(若被淘汰页在内存期间被修改过,则要将其写回外存)。
缺页中断作为中断,同样要经历诸如保护CPU环境、分析中断原因、转入缺页中断处理程序、恢复CPU环境等几个步骤。但与一般的中断相比,它有以下两个明显的区别:
请求分页系统中的地址变换机构,是在分页系统地址变换机构的基础上,为实现虚拟内存,又增加了某些功能而形成的。
如图3.21所示,在进行地址变换时,先检索快表:

进程运行时,若其访问的页面不在内存中而需将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区。
选择调出页面的算法就称为页面置换算法。好的页面置换算法应有较低的页面更换频率也就是说,应将以后不会再访问或以后较长时间内不会再访问的页面先调出。
常见的置换算法有以下4种。
最佳(Optimal,OPT)置换算法选择的被淘汰页面是以后永不使用的页面,或是在最长时间内不再被访问的页面,以便保证获得最低的缺页率。然而,由于人们目前无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。
最佳置换算法可用来评价其他算法。假定系统为某进程分配了三个物理块,并考虑有页面号引用串7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1。进程运行时,先将7,0,1三个页面依次装入内存。进程要访问页面2时,产生缺页中断,根据最佳置换算法,选择将第18次访问才需调入的页面7淘汰。然后,访问页面0时,因为它已在内存中,所以不必产生缺页中断。访问页面3时,又会根据最佳置换算法将页面1淘汰……以此类推,如图3.22所示,从图中可以看出采用最佳置换算法时的情况。

最长时间不被访问和以后被访问次数最小是不同的概念,初学者在理解OPT 算法时千万不要混淆。
可以看到,发生缺页中断的次数为9,页面置换的次数为6。
优先淘汰最早进入内存的页面,即在内存中驻留时间最久的页面。该算法实现简单,只需把调入内存的页面根据先后次序链接成队列,设置一个指针总指向最早的页面。但该算法与进程实际运行时的规律不适应,因为在进程中,有的页面经常被访问。
这里仍用上面的实例采用FIFO算法进行页面置换。进程访问页面2时,把最早进入内存的页面7换出。然后访问页面3时,把2,0,1中最先进入内存的页面0换出。由图3.23可以看出,利用FIFO算法时进行了12次页面置换,比最佳置换算法正好多一倍。
FIFO算法还会产生所分配的物理块数增大而页故障数不减反增的异常现象,这由Belady于1969年发现,因此称为Belady异常。只有FIFO算法可能出现 Belady异常,LRU和OPT算法永远不会出现Belady 异常。

如图3.24所示,页面访问顺序为3,2,1,0,3,2,4,3,2,1,0,4。若采用FIFO置换算法,当分配的物理块为3个时,缺页次数为9次;当分配的物理块为4个时,缺页次数为10次。分配给进程的物理块增多,但缺页次数不减反增。

选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问。该算法为每个页面设置一个访问字段,来记录页面自上次被访问以来所经历的时间,淘汰页面时选择现有页面中值最大的予以淘汰。
再对上面的实例采用LRU算法进行页面置换,如图3.25所示。进程第一次对页面2访问时,将最近最久未被访问的页面7置换出去。然后在访问页面3时,将最近最久未使用的页面1换出。

在图3.25中,前5次置换的情况与最佳置换算法相同,但两种算法并无必然联系。实际上,LRU算法根据各页以前的情况,是“向前看”的,而最佳置换算法则根据各页以后的使用情况,是“向后看”的。
LRU算法的性能较好,但需要寄存器和栈的硬件支持。LRU是堆栈类的算法。理论上可以证明,堆栈类算法不可能出现Belady异常。FIFO算法基于队列实现,不是堆栈类算法。
LRU算法的性能接近于OPT算法,但实现起来比较困难,且开销大;FIFO算法实现简单,但性能差。因此,操作系统的设计者尝试了很多算法,试图用比较小的开销接近LRU算法的性能,这类算法都是CLOCK算法的变体。因为算法要循环扫描缓冲区,像时钟的指针一样转动,所以称为CLOCK算法。
简单的CLOCK算法给每帧关联一个附加位,称为使用位。当某页首次装入主存时,将该帧的使用位设置为1;当该页随后再被访问到时,其使用位也被置为1。对于页替换算法,用于替换的候选帧集合可视为一个循环缓冲区,并有一个指针与之相关联。当某一页被替换时,该指针被设置成指向缓冲区中的下一帧。当需要替换一页时,操作系统扫描缓冲区,以查找使用位被置为0的一帧。每当遇到一个使用位为1的啊时,操作系统就付以拉旒里刷,所右帧的使用位均为1,始时,缓冲区中所有帧的使用位均为0,则选择遇到的第一个帧替换;若所有帧的使用位均为1,
则指针在缓冲区中完整地循环一周,把所有使用位都置为0,并停留在最初的位置上,替换该帧中的页。由于该算法循环检查各页面的情况,因此称CLOCK算法,又称最近未用(Not RecentlyUsed,NRU)算法。
CLOCK算法的性能比较接近LRU算法,而通过增加使用的位数目,可以使得CLOCK算法更加高效。在使用位的基础上再增加一个修改位,则得到改进型CLOCK置换算法。这样,每帧都处于以下4种情况之一:
算法执行如下操作步骤:
1)从指针的当前位置开始,扫描帧缓冲区。在这次扫描过程中,对使用位不做任何修改。选择遇到的第一个帧(u =0, m = 0)用于替换。
2)若第1)步失败,则重新扫描,查找(u=0,m= 1)的帧。选择遇到的第一个这样的帧用于替换。在这个扫描过程中,对每个跳过的帧,把它的使用位设置成0。
3)若第2)步失败,则指针将回到它的最初位置,且集合中所有帧的使用位均为0。重复第1)步,并且若有必要,重复第2)步,以便可以找到供替换的帧。
改进型CLOCK算法优于简单CLOCK算法的地方在于替换时首选没有变化的页。由于修改过的页在被替换之前必须写回,因而这样做会节省时间。
有些读者会认为CLOCK算法和改进型CLOCK算法记忆起来不易。为方便记忆,我们将其总结如下。
操作系统中任何经过优化而有效的页面置换算法都有一个原则,即尽可能保留曾经使用过的页面,而淘汰未使用的页面,认为这样可以在总体上减少换页次数。CLOCK 算法只考虑到是否被访问过,因此被访问过的当然尽可能留下,未使用过的就淘汰;而改进型CLOCK算法对使用过的页面又做了细分,分为使用过但未修改过和使用过且修改过。因此,若有未使用过的页面,则当然首先把它换出,若全部页面都使用过,则当然优先把未修改过的页面换出。
为帮助读者理解,这里举一个例子。假设系统给某进程分配了5个页框,刚开始,进程依次访问1,3,4,2,5号页面,系统会将这些页面连成一个循环队列,刚开始扫描指针指向第一个被访问的页面(即1号页),如图3.26所示。

图3.26中,小括号内的数字就是使用位。接下来,若进程请求访问6号页面,则由于此时分配给进程的5个页框都被使用,因此必须选择一个页面置换出去。按照CLOCK置换算法的规则,在第一轮扫描中,指针扫过的页面的使用位应置为0。第一轮扫描的过程如图3.27所示。

第一轮扫描中,未找到使用位为0的页面,因此需要进行第二轮扫描。第二轮扫描中,1号页面的使用位为0,因此将1号页面换出,将6号页面换入,将6号页的访问位设置为1,并将扫描指针后移(若下次需要换出页面,则从3号页面开始扫描),如图3.28所示。
注意一个小细节:假设1号页面原先占有的是 $x$ 号物理块(页框),则6号页面换入内存后也放在 $x$ 号物理块中。

对于分页式的虚拟内存,在进程准备执行时,不需要也不可能把一个进程的所有页都读入主存。因此,操作系统必须决定读取多少页,即决定给特定的进程分配几个页框。给一个进程分配的物理页框的集合就是这个进程的驻留集。需要考虑以下几点:
1)分配给一个进程的存储量越小,任何时候驻留在主存中的进程数就越多,从而可以提高处理机的时间利用效率。
2)若一个进程在主存中的页数过少,则尽管有局部性原理,页错误率仍然会相对较高。
3)若页数过多,则由于局部性原理,给特定的进程分配更多的主存空间对该进程的错误率没有明显的影响。
基于这些因素,现代操作系统通常采用三种策略:
1)固定分配局部置换。它为每个进程分配一定数目的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后调入需要的页面。实现这种策略时,难以确定应为每个进程分配的物理块数目:太少会频繁出现缺页中断,太多又会使CPU和其他资源利用率下降。
2)可变分配全局置换。这是最易于实现的物理块分配和置换策略,它为系统中的每个进程分配一定数目的物理块,操作系统自身也保持一个空闲物理块队列。当某进程发生缺页时,系统从空闲物理块队列中取出一个物理块分配给该进程,并将欲调入的页装入其中。这种方法比固定分配局部置换更加灵活,可以动态增加进程的物理块,但也存在弊端如它会盲目地给进程增加物理块,从而导致系统多道程序的并发能力下降。
3)可变分配局部置换。它为每个进程分配一定数目的物理块,当某个进程发生缺页时,只允许从该进程在内存的页面中选出一页换出,因此不会影响其他进程的运行。若进程在运行中频繁地缺页,则系统再为该进程分配若干物理块,直至该进程缺页率趋于适当程度;反之,若进程运行中的缺页率特别低,则可适当减少分配给该进程的物理块。比起可变分配全局置换,这种方法不仅可以动态增加进程物理块的数量,还能动态减少进程物理块的数量,在保证进程不会过多地调页的同时,也保持了系统的多道程序并发能力。当然它需要更复杂的实现,也需要更大的开销,但对比频繁地换入/换出所浪费的计算机资源,这种牺牲是值得的。
页面分配策略在2015年的统考选择题中出现过,考查的是这三种策略的名称。往年很多读者看到这里时,由于认为不是重点,复习时便一带而过,最后在考试中失分。在这种基础题上失分是十分可惜的。再次提醒读者,考研成功的秘诀在于“反复多次”和“全面”。
为确定系统将进程运行时所缺的页面调入内存的时机,可采取以下两种调页策略:
1)预调页策略。根据局部性原理,一次调入若干相邻的页可能会比一次调入一页更高效。但若调入的一批页面中大多数都未被访问,则又是低效的。因此,需要采用以预测为基础的预调页策略,将预计在不久之后便会被访问的页面预先调入内存。但目前预调页的成功率仅约50%。因此这种策略主要用于进程的首次调入,由程序员指出应先调入哪些页。
2)请求调页策略。进程在运行中需要访问的页面不在内存而提出请求,由系统将所需页面调入内存。由这种策略调入的页一定会被访问,且这种策略比较易于实现,因此在目前的虚拟存储器中大多采用此策略。它的缺点是每次只调入一页,调入/调出页面数多时会花费过多的IO开销。
预调入实际上就是运行前的调入,请求调页实际上就是运行期间调入。一般情况下,两种调页策略会同时使用。
请求分页系统中的外存分为两部分:用于存放文件的文件区和用于存放对换页面的对换区。对换区通常采用连续分配方式,而文件区采用离散分配方式,因此对换区的磁盘IO速度比文件区的更快。这样,从何处调入页面就存在三种情况:
1)系统拥有足够的对换区空间。可以全部从对换区调入所需页面,以提高调页速度。为此,在进程运行前,需将与该进程有关的文件从文件区复制到对换区。
2)系统缺少足够的对换区空间。凡不会被修改的文件都直接从文件区调入;而当换出这些页面时,由于它们未被修改而不必再将它们换出。但对于那些可能被修改的部分,在将它们换出时须调到对换区,以后需要时再从对换区调入(因为读的速度比写的速度快)
3)UNIX方式。与进程有关的文件都放在文件区,因此未运行过的页面都应从文件区调入曾经运行过但又被换出的页面,由于放在对换区,因此下次调入时应从对换区调入。进程请求的共享页面若被其他进程调入内存,则无须再从对换区调入。
在页面置换过程中,一种最糟糕的情形是,刚刚换出的页面马上又要换入主存,刚刚换入的页面马上又要换出主存,这种频繁的页面调度行为称为抖动或颠簸。若一个进程在换页上用的时间多于执行时间,则这个进程就在颠簸。
频繁发生缺页中断(抖动)的主要原囚定,术个H在么你放离在稳定状态,几乎主存的所帧数目。虚拟内存技术可在内仔中保留史多的心在坟方问列尽可能多的进程。然而,如果管理不有空间都被进程块占据,处理机和操作系统可以且按w四到可的操3作而不是执行讲程的指令,当,那么处理机的大部分时间都将用于交换块,即请求调入贝面的操作,如个定执1进在了,因此会大大降低系统效率。
工作集是指在某段时间间隔内,进程要访问的页面集合。基于局部性原理,可以用最近访问过的页面来确定工作集。一般来说,工作集W可由时间t和工作集窗口大小△来确定。例如,某进程对页面的访问次序如下:

本小节引入一个实例来说明虚实地址的变换过程,考虑到统考试题近来出现了学科综合的趋势,这里结合“计算机组成原理”中的Cache部分进行讲解。对于不参加统考的读者,可以看到翻译出实地址为止,对于参加统考却还没有复习计算机组成原理的读者,可在复习完“计算机组成原理”后,再回来看本章的内容。
设某系统满足以下条件:

上一节提到过,多道程序并发执行不仅使进程之间共享了处理器,而且同时共享了主存。然而,随着对处理器需求的增长,进程的执行速度会以某种合理平滑的方式慢下来。但是,若同时运行的进程太多,则需要很多的内存,当一个程序没有内存空间可用时,那么它甚至无法运行。所以,在物理上扩展内存相对有限的条件下,应尝试以一些其他可行的方式在逻辑上扩充内存。
虚存的容量要满足以下两个条件:
①虚存的实际容量≤内存容量和外存容量之和,这是硬件的硬性条件规定的,若虚存的实际容量超过了这个容量,则没有相应的空间来供虚存使用。
${\textstyle\unicode{x2461}}$ 虚存的最大容量≤计算机的地址位数能容纳的最大容量。假设地址是32位的,按字节编址,一个地址代表1B存储空间,则虚存的最大容量≤4GB( $2^{32}$ B)。这是因为若虚存的最大容量超过4GB,则32位的地址将无法访问全部虚存,也就是说4GB 以后的空间被浪费了,相当于没有一样,没有任何意义。
实际虚存的容量是取条件①和②的交集,即两个条件都要满足,仅满足一个条件是不行的。
虚拟内存使用外存上的空间来扩充内存空间,通过一定的换入/换出,使得整个系统在逻辑上能够使用一个远远超出其物理内存大小的内存容量。因为虚拟内存技术调换页面时需要访问外存,会导致平均访存时间增加,若使用了不合适的替换算法,则会大大降低系统性能。
本节学习了4种页面置换算法,要把它们与处理机调度算法区分开。当然,这些调度算法之间也是有联系的,它们都有一个共同点,即通过一定的准则决定资源的分配对象。在处理机调度算法中这些准则比较多,有优先级、响应比、时间片等,而在页面调度算法中就比较简单,即是否被用到过或近段时间内是否经常使用。在操作系统中,几乎每类资源都会有相关的调度算法,读者通过将这些调度算法作为线索,可把整个操作系统的课程连成一个整体。
【考纲内容】
(一)进程与线程
进程的概念;进程的状态与转换
进程控制;进程组织
进程通信;线程概念与多线程模型
(二)处理机调度
调度的基本概念;调度时机、切换与过程
调度的基本准则;调度方式;典型调度算法
(三)进程同步
进程同步的基本概念
实现临界区互斥的基本方法
信号量;管程;经典同步问题
(四)死锁
死锁的概念;死锁处理策略
死锁预防;死锁避免;死锁的检测和解除
【知识框架】
【复习提示】
进程管理是操作系统的核心,也是每年必考的重点。其中,进程的概念、进程调度、信号量机制实现同步和互斥、进程死锁等更是重中之重,必须深入掌握。需要注意的是,除选择题外,本章还容易出综合题,其中信号量机制实现同步和互斥、进程调度算法和银行家算法都是可能出现的综合题考点,如利用信号量进行进程同步就在往年的统考中频繁出现。
进程:process
在学习本节时,请读者思考以下问题:
1)为什么要引入进程?
2)什么是进程?进程由什么组成?
3)进程是如何解决问题的?
希望读者带着上述问题去学习本节内容,并在学习的过程中多思考,从而更深入地理解本节内容。进程本身是一个比较抽象的概念,它不是实物,看不见、摸不着,初学者在理解进程概念时存在一定困难,在介绍完进程的相关知识后,我们会用比较直观的例子帮助大家理解。
在多道程序环境下,允许多个程序并发执行,此时它们将失去封闭性,并具有间断性及不可再现性的特征。为此引入了进程(Process)的概念,以便更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性(最基本的两个特性)。
为了使参与并发执行的程序(含数据)能独立地运行,必须为之配置一个专门的数据结构,称为进程控制块(Process Control Block,PCB)。系统利用PCB来描述进程的基本情况和运行状态,进而控制和管理进程。相应地,由程序段、相关数据段和PCB三部分构成了进程映像(进程实体)。所谓创建进程,实质上是创建进程映像中的PCB;而撤销进程,实质上是撤销进程的PCB。值得注意的是,进程映像是静态的,进程则是动态的。
注意:PCB是进程存在的唯一标志!
从不同的角度,进程可以有不同的定义,比较典型的定义有:
1)进程是程序的一次执行过程。
2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
3)进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
引入进程实体的概念后,我们可以把传统操作系统中的进程定义为:“进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。”
读者要准确理解这里说的系统资源。它指处理机、存储器和其他设备服务于某个进程的“时间”,例如把处理机资源理解为处理机的时间片才是准确的。因为进程是这些资源分配和调度的独立单位,即“时间片”分配的独立单位,这就决定了进程一定是一个动态的、过程性的概念。
进程是由多道程序的并发执行而引出的,它和程序是两个截然不同的概念。进程的基本特征是对比单个程序的顺序执行提出的,也是对进程管理提出的基本要求。
1)动态性。进程是程序的一次执行,它有着创建、活动、暂停、终止等过程,具有一定的生命周期,是动态地产生、变化和消亡的。动态性是进程最基本的特征。
2)并发性。指多个进程实体同时存于内存中,能在一段时间内同时运行。并发性是进程的重要特征,同时也是操作系统的重要特征。引入进程的目的就是使程序能与其他进程的程序并发执行,以提高资源利用率。
3)独立性。指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。凡未建立PCB的程序,都不能作为一个独立的单位参与运行。
4)异步性。由于进程的相互制约,使得进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。异步性会导致执行结果的不可再现性,为此在操作系统中必须配置相应的进程同步机制。
5)结构性。每个进程都配置一个PCB对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制块三部分组成的。
通常不会直接考查进程有什么特性,所以读者对上面的5个特性不求记忆,只求理解。
进程在其生命周期内,由于系统中各进程之间的相互制约关系及系统的运行环境的变化,使得进程的状态也在不断地发生变化(一个进程会经历若干不同状态)。通常进程有以下5种状态,前3种是进程的基本状态。
1)运行态。进程正在处理机上运行。在单处理机环境下,每个时刻最多只有一个进程处于运行态。
2)就绪态。进程获得了除处理机外的一切所需资源,一旦得到处理机,便可立即运行。系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
3)阻塞态,又称等待态。进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理机)或等待输入/输出完成。即使处理机空闲,该进程也不能运行。
4)创建态。进程正在被创建,尚未转到就绪态。创建进程通常需要多个步骤:首先申请一个空白的 PCB,并向PCB中填写一些控制和管理进程的信息;然后由系统为该进程分配运行时所必需的资源;最后把该进程转入就绪态。
5)结束态。进程正从系统中消失,可能是进程正常结束或其他原因中断退出运行。进程需要结束运行时,系统首先必须将该进程置为结束态,然后进一步处理资源释放和回收等工作。
注意区别就绪态和等待态:就绪态是指进程仅缺少处理机,只要获得处理机资源就立即运行;而等待态是指进程需要其他资源(除了处理机)或等待某一事件。之所以把处理机和其他资源划分开,是因为在分时系统的时间片轮转机制中,每个进程分到的时间片是若干毫秒。也就是说,进程得到处理机的时间很短且非常频繁,进程在运行过程中实际上是频繁地转换到就绪态的;而其他资源(如外设)的使用和分配或某一事件的发生(如IO操作的完成)对应的时间相对来说很长,进程转换到等待态的次数也相对较少。这样来看,就绪态和等待态是进程生命周期中两个完全不同的状态,显然需要加以区分。
图2.1说明了5种进程状态的转换,而3种基本状态之间的转换如下:

需要注意的是,一个进程从运行态变成阻塞态是主动的行为,而从阻塞态变成就绪态是被动的行为,需要其他相关进程的协助。
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。在操作系统中,一般把进程控制用的程序段称为原语,原语的特点是执行期间不允许中断,它是一个不可分割的基本单位。
允许一个进程创建另一个进程。此时创建者称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程那里获得的资源归还给程。此外,在撤销父进程时,必须同时撤销其所有的子进程。
在操作系统中,终端用户登录系统、作业调度、系统提供服务、用户程序的应用请求等都会引起进程的创建。操作系统创建一个新进程的过程如下(创建原语):
1)为新进程分配一个唯一的进程标识号,并申请一个空白的 PCB (PCB是有限的)。若申请失败,则创建失败。
2)为进程分配资源,为新进程的程序和数据及用户栈分配必要的内存空间(在PCB中体现)。注意,若资源不足(如内存空间),则并不是创建失败,而是处于阻塞态,等待内存资源。
3)初始化 PCB,主要包括初始化标志信息、初始化处理机状态信息和初始化处理机控制信息,以及设置进程的优先级等。
4)若进程就绪队列能够接纳新进程,则将新进程插入就绪队列,等待被调度运行。
引起进程终止的事件主要有:①正常结束,表示进程的任务已完成并准备退出运行。②异常结束,表示进程在运行时,发生了某种异常事件,使程序无法继续运行,如存储区越界、保护错、非法指令、特权指令错、运行超时、算术运算错、I/O故障等。③外界干预,指进程应外界的请求而终止运行,如操作员或操作系统干预、父进程请求和父进程终止。
操作系统终止进程的过程如下(撤销原语):
1)根据被终止进程的标识符,检索PCB,从中读出该进程的状态。
2)若被终止进程处于执行状态,立即终止该进程的执行,将处理机资源分配给其他进程。
3)若该进程还有子孙进程,则应将其所有子孙进程终止。
4)将该进程所拥有的全部资源,或归还给其父进程,或归还给操作系统。
5)将该PCB从所在队列(链表)中删除。
正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作可做等,由系统自动执行阻塞原语(Block),使自己由运行态变为阻塞态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU才可能将其转为阻塞态。阻塞原语的执行过程如下:
1)找到将要被阻塞进程的标识号对应的PCB。
2)若该进程为运行态,则保护其现场,将其状态转为阻塞态,停止运行。3)把该PCB插入相应事件的等待队列,将处理机资源调度给其他就绪进程。
当被阻塞进程所期待的事件出现时,如它所启动的IO操作已完成或其所期待的数据已到达,由有关进程(比如,释放该IO 设备的进程,或提供数据的进程)调用唤醒原语(Wakeup),将等待该事件的进程唤醒。唤醒原语的执行过程如下:
1)在该事件的等待队列中找到相应进程的PCB。
2)将其从等待队列中移出,并置其状态为就绪态。
3)把该PCB插入就绪队列,等待调度程序调度。
需要注意的是,Block原语和 Wakeup原语是一对作用刚好相反的原语,必须成对使用。Block原语是由被阻塞进程自我调用实现的,而 Wakeup原语则是由一个与被唤醒进程合作或被其他相关的进程调用实现的。
对于通常的进程而言,其创建、撤销及要求由系统设备完成的I/O操作,都是利用系统调用而进入内核,再由内核中的相应处理程序予以完成的。进程切换同样是在内核的支持下实现的,因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。
进程切换是指处理机从一个进程的运行转到另一个进程上运行,在这个过程中,进程的运行环境产生了实质性的变化。进程切换的过程如下:
1))保存处理机上下文,包括程序计数器和其他寄存器。
2)更新PCB信息。
3)把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
4)选择另一个进程执行,并更新其PCB。
5)更新内存管理的数据结构。
6)恢复处理机上下文。
注意,进程切换与处理机模式切换是不同的,模式切换时,处理机逻辑上可能还在同一进程中运行。若进程因中断或异常进入核心态运行,执行完后又回到用户态刚被中断的程序运行,则操作系统只需恢复进程进入内核时所保存的CPU现场,而无须改变当前进程的环境信息。但若要切换进程,当前运行进程改变了,则当前进程的环境信息也需要改变。
进程是一个独立的运行单位,也是操作系统进行资源分配和调度的基本单位。它由以下三部分组成,其中最核心的是进程控制块(PCB)。
进程创建时,操作系统为它新建一个PCB,该结构之后常驻内存,任意时刻都可以存取,并在进程结束时删除。PCB是进程实体的一部分,是进程存在的唯一标志。
进程执行时,系统通过其 PCB 了解进程的现行状态信息,以便对其进行控制和管理;进程结束时,系统收回其PCB,该进程随之消亡。操作系统通过PCB表来管理和控制进程。
当操作系统欲调度某进程运行时,要从该进程的PCB中查出其现行状态及优先级;在调度到某进程后,要根据其PCB中所保存的处理机状态信息,设置该进程恢复运行的现场,并根据其PCB中的程序和数据的内存始址,找到其程序和数据;进程在运行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也需要访问PCB;当进程由于某种原因而暂停运行时,又需将其断点的处理机环境保存在PCB中。可见,在进程的整个生命期中,系统总是通过PCB对进程进行控制的,亦即系统唯有通过进程的PCB才能感知到该进程的存在。
表2.1是一个PCB 的实例。PCB主要包括进程描述信息、进程控制和管理信息、资源分配清单和处理机相关信息等。各部分的主要说明如下:

1)进程描述信息。进程标识符:标志各个进程,每个进程都有一个唯一的标识号。用户标识符:进程归属的用户,用户标识符主要为共享和保护服务。
2)进程控制和管理信息。进程当前状态:描述进程的状态信息,作为处理机分配调度的依据。进程优先级:描述进程抢占处理机的优先级,优先级高的进程可优先获得处理机。
3)资源分配清单,用于说明有关内存地址空间或虚拟地址空间的状况,所打开文件的列表和所使用的输入/输出设备信息。
4)处理机相关信息,主要指处理机中各寄存器的值,当进程被切换时,处理机状态信息都必须保存在相应的PCB中,以便在该进程重新执行时,能从断点继续执行。
在一个系统中,通常存在着许多进程的PCB,有的处于就绪态,有的处于阻塞态,而且阻塞的原因各不相同。为了方便进程的调度和管理,需要将各进程的PCB用适当的方法组织起来。目前,常用的组织方式有链接方式和索引方式两种。链接方式将同一状态的PCB链接成一个队列,不同状态对应不同的队列,也可把处于阻塞态的进程的PCB,根据其阻塞原因的不同,排成多个阻塞队列。索引方式将同一状态的进程组织在一个索引表中,索引表的表项指向相应的PCB,不同状态对应不同的索引表,如就绪索引表和阻塞索引表等。
程序段就是能被进程调度程序调度到CPU执行的程序代码段。注意,程序可被多个进程共享,即多个进程可以运行同一个程序。
一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果。
进程通信是指进程之间的信息交换。PV操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的通信方式。高级通信方法主要有以下三类。
在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换,如图2.2所示。在对共享空间进行写/读操作时,需要使用同步互斥工具(如Р操作、V操作),对共享空间的写/读进行控制。共享存储又分为两种:低级方式的共享是基于数据结构的共享;高级方式的共享则是基于存储区的共享。操作系统只负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排读/写指令完成。
注意,用户进程空间一般都是独立的,进程运行期间一般不能访问其他进程的空间,要想让两个用户进程共享空间,必须通过特殊的系统调用实现,而进程内的线程是自然共享进程空间的。
简单理解就是,甲和乙中间有一个大布袋,甲和乙交换物品是通过大布袋进行的,甲把物品放在大布袋里,乙拿走。但乙不能直接到甲的手中拿东西,甲也不能直接到乙的手中拿东西。
在消息传递系统中,进程间的数据交换是以格式化的消息(Message)为单位的。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。
1)直接通信方式。发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息,如图2.3所示。

2)间接通信方式。发送进程把消息发送到某个中间实体,接收进程从中间实体取得消息。
这种中间实体一般称为信箱,这种通信方式又称信箱通信方式。该通信方式广泛应用于计算机网络中,相应的通信系统称为电子邮件系统。
简单理解就是,甲要告诉乙某些事情,就要写信,然后通过邮差送给乙。直接通信就是邮差把信直接送到乙的手上;间接通信就是乙家门口有一个邮箱,邮差把信放到邮箱里。
管道通信是消息传递的一种特殊方式(见图2.4)。所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件,又名pipe文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程)则从管道中接收(读)数据。为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。

下面以Linux中的管道为例进行说明。在Linux 中,管道是一种使用非常频繁的通信机制。从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现如下:
1)限制管道的大小。实际上,管道是一个固定大小的缓冲区。在 Linux中,该缓冲区的大小为4KB,这使得它的大小不像文件那样不加检验地增长。使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,这种情况发生时,随后对管道的 write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供 write()调用写。
2)读进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。
注意:从管道读数据是一次性操作,数据一旦被读取,它就从管道中被抛弃,释放空间以便写更多的数据。管道只能采用半双工通信,即某一时刻只能单向传输。要实现父子进程双方互动通信,需要定义两个管道。
管道可以理解为共享存储的优化和发展,因为在共享存储中,若某进程要访问共享存储空间则必须没有其他进程在该共享存储空间中进行写操作,否则访问行为就会被阻塞。而管道通信中存储空间进化成了缓冲区,缓冲区只允许一边写入、另一边读出,因此只要缓冲区中有数据,进程就能从缓冲区中读出,而不必担心会因为其他进程在其中进行写操作而遭到阻塞,因为写进程会先把缓冲区写满,然后才让读进程读,当缓冲区中还有数据时,写进程不会往缓冲区写数据。当然,这也决定了管道通信必然是半双工通信。
引入进程的目的是更好地使多道程序并发执行,提高资源利用率和系统吞吐量;而引入线程的目的则是减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
线程最直接的理解就是“轻量级进程”,它是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。
引入线程后,进程的内涵发生了改变,进程只作为除CPU外的系统资源的分配单元,而线程则作为处理机的分配单元。由于一个进程内部有多个线程,若线程的切换发生在同一个进程内部,则只需要很少的时空开销。
1)调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是拥有资源的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。
2)拥有资源。不论是传统操作系统还是设有线程的操作系统,进程都是拥有资源的基本单位,而线程不拥有系统资源(也有一点儿必不可少的资源),但线程可以访问其隶属进程的系统资源。要知道,若线程也是拥有资源的单位,则切换线程就需要较大的时空开销,线程这个概念的提出就没有意义。
3)并发性。在引入线程的操作系统中,不仅进程之间可以并发执行,而且多个线程之间也可以并发执行,从而使操作系统具有更好的并发性,提高了系统的吞吐量。
4)系统开销。由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、IO设备等,因此操作系统所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程CPU 环境的保存及新调度到进程CPU环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。此外,由于同一进程内的多个线程共享进程的地址空间,因此这些线程之间的同步与通信非常容易实现,甚至无须操作系统的干预。
5)地址空间和其他资源(如打开的文件)。进程的地址空间之间互相独立,同一进程的各线程间共享进程的资源,某进程内的线程对于其他进程不可见。
6)通信方面。进程间通信(IPC)需要进程同步和互斥手段的辅助,以保证数据的一致性,而线程间可以直接读/写进程数据段(如全局变量)来进行通信。
多线程操作系统把线程作为独立运行(或调度)的基本单位,此时的进程已不再是一个基本的可执行实体,但它仍具有与执行相关的状态。所谓进程处于“执行”状态,实际上是指该进程中的某线程正在执行。线程的主要属性如下:
1)线程是一个轻型实体,它不拥有系统资源,但每个线程都应有一个唯一的标识符和一个线程控制块,线程控制块记录了线程执行的寄存器和栈等现场状态。
2)不同的线程可以执行相同的程序,即同一个服务程序被不同的用户调用时,操作系统把它们创建成不同的线程。
3)同一进程中的各个线程共享该进程所拥有的资源。
4)线程是处理机的独立调度单位,多个线程是可以并发执行的。在单CPU的计算机系统中,各线程可交替地占用CPU;在多CPU的计算机系统中,各线程可同时占用不同的CPU,若各个CPU同时为一个进程内的各线程服务,则可缩短进程的处理时间。
5)一个线程被创建后,便开始了它的生命周期,直至终止。线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态变化。
为什么线程的提出有利于提高系统并发性?可以这样来理解:由于有了线程,线程切换时,有可能会发生进程切换,也有可能不发生进程切换,平均而言每次切换所需的开销就变小了,因此能够让更多的线程参与并发,而不会影响到响应时间等问题。
线程的实现可以分为两类:用户级线程(User-Level Thread,ULT)和内核级线程(Kernel-LevelThread,KLT)。内核级线程又称内核支持的线程。
在用户级线程中,有关线程管理(线程的创建、撤销和切换等)的所有工作都由应用程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程开始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。图2.5(a)说明了用户级线程的实现方式。
在内核级线程中,线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也在内核基于线程架构的基础上完成。图2.5(b)说明了内核级线程的实现方式。
有些系统中使用组合方式的多线程实现。线程创建完全在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户级线程被映射到一些(小于等于用户级线程的数目)内核级线程上。图2.5(c)说明了用户级与内核级的组合实现方式。

有些系统同时支持用户线程和内核线程,由此产生了不同的多线程模型,即实现用户级线程和内核级线程的连接方式。
1)多对一模型。将多个用户级线程映射到一个内核级线程,线程管理在用户空间完成。此模式中,用户级线程对操作系统不可见(即透明)。
优点:线程管理是在用户空间进行的,因而效率比较高。
缺点:一个线程在使用内核服务时被阻塞,整个进程都会被阻塞;多个线程不能并行地运行在多处理机上。
2)一对一模型。将每个用户级线程映射到一个内核级线程。
优点:当一个线程被阻塞后,允许另一个线程继续执行,所以并发能力较强。
缺点:每创建一个用户级线程都需要创建一个内核级线程与其对应,这样创建线程的开销比较大,会影响到应用程序的性能。
3)多对多模型。将n个用户级线程映射到m个内核级线程上,要求m≤n。
特点:多对多模型是多对一模型和一对一模型的折中,既克服了多对一模型并发度不高的缺点,又克服了一对一模型的一个用户进程占用太多内核级线程而开销太大的缺点。
此外,还拥有多对一模型和一对一模型各自的优点,可谓集两者之所长。
在多道程序同时运行的背景下,进程之间需要共享系统资源,因此会导致各程序在执行过程中出现相互制约的关系,程序的执行会表现出间断性的特征。这些特征都是在程序的执行过程中发生的,是动态的过程,而传统的程序本身是一组指令的集合,是一个静态的概念,无法描述程序在内存中的执行情况,即我们无法从程序的字面上看出它何时执行、何时停顿,也无法看出它与其他执行程序的关系,因此,程序这个静态概念已不能如实反映程序并发执行过程的特征。为了深刻描述程序动态执行过程的性质乃至更好地支持和管理多道程序的并发执行,人们引入了进程的概念。
进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码本身,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。
一个进程实体由程序段、相关数据段和 PCB三部分构成,其中 PCB是标志一个进程存在的唯一标识,程序段是进程运行的程序的代码,数据段则存储程序运行过程中相关的一些数据。
进程把能够识别程序运行态的一些变量存放在 PCB中,通过这些变量系统能够更好地了解进程的状况,并在适当时进行进程的切换,以避免一些资源的浪费,甚至划分为更小的调度单位一线程来提高系统的并发度。
本节主要介绍什么是进程,并围绕这个问题进行一些阐述和讨论,为下一节讨论的内容做铺垫,但之前未学过相关课程的读者可能会比较费解,到现在为止对进程这个概念还未形成比较清晰的认识。接下来,我们再用一个比较熟悉的概念来类比进程,以便大家能彻底理解本节的内容到底在讲什么,到底解决了什么问题。
我们用“人的生命历程”来类比进程。首先,人的生命历程一定是一个动态的、过程性的概念,要研究人的生命历程,先要介绍经历这个历程的主体是什么。主体当然是人,相当于经历进程的主体是进程映像,人有自己的身份,相当于进程映像里有PCB;人生历程会经历好几种状态:出生的时候、弥留的时候、充满斗志的时候、发奋图强的时候及失落的时候,相当于进程有创建、撤销、就绪、运行、阻塞等状态,这几种状态会发生改变,人会充满斗志而转向发奋图强,发奋图强获得进步之后又会充满斗志预备下一次发奋图强,或者发奋图强后遇到阻碍会进入失落状态,然后在别人的开导之下又重新充满斗志。类比进程,会由就绪态转向运行态,运行态转向就绪态,或者运行态转向阻塞态,然后在别的进程帮助下返回就绪态。
若我们用“人生历程”这个过程的概念去类比进程,则对进程的理解就会更深一层。前面生活化的例子可以帮我们理解进程的实质,但它毕竟有不严谨的地方。一种较好的方式是,在类比进程和“人生历程”后,再看一遍前面较为严谨的书面阐述和讨论,这样对知识的掌握会更加准确而全面。
这里再给出一些学习计算机科学知识的建议。学习科学知识时,很多同学会陷入一个误区,即只注重对定理、公式的应用,而忽视对基础概念的理解。这是我们从小到大为了应付考试而培养出的一个毛病,因为熟练应用公式和定理对考试有立竿见影的效果。公式、定理的应用固然重要,但基础概念的理解能让我们透彻地理解一门学科,更利于我们产生兴趣,培养创造性思维。
在学习本节时,请读者思考以下问题:
1)为什么要进行处理机调度?
2)调度算法有哪几种?结合第1章学习的分时操作系统和实时操作系统,思考哪种调度算法比较适合这两种操作系统。
希望读者能够在学习调度算法前,先自己思考一些调度算法,在学习的过程中注意把自己的想法与这些经典的算法进行比对,并学会计算一些调度算法的周转时间。
在多道程序系统中,进程的数量往往多于处理机的个数,因此进程争用处理机的情况在所难免。处理机调度是对处理机进行分配,即从就绪队列中按照一定的算法(公平、高效)选择一个进程并将处理机分配给它运行,以实现进程并发地执行。
处理机调度是多道程序操作系统的基础,是操作系统设计的核心问题。
一个作业从提交开始直到完成,往往要经历以下三级调度,如图2.6所示。
1)作业调度。又称高级调度,其主要任务是按一定的原则从外存上处于后备状态的作业中挑选一个(或多个)作业,给它(们)分配内存、输入/输出设备等必要的资源,并建立相应的进程,以使它(们)获得竞争处理机的权利。简言之,作业调度就是内存与辅存之间的调度。对于每个作业只调入一次、调出一次。
多道批处理系统中大多配有作业调度,而其他系统中通常不需要配置作业调度。作业调度的执行频率较低,通常为几分钟一次。
2)中级调度。又称内存调度,其作用是提高内存利用率和系统吞吐量。为此,应将那些暂时不能运行的进程调至外存等待,把此时的进程状态称为挂起态。当它们已具备运行条件且内存又稍有空闲时,由中级调度来决定把外存上的那些已具备运行条件的就绪进程再重新调入内存,并修改其状态为就绪态,挂在就绪队列上等待。
3)进程调度。又称低级调度,其主要任务是按照某种方法和策略从就绪队列中选取一个进程,将处理机分配给它。进程调度是操作系统中最基本的一种调度,在一般的操作系统中都必须配置进程调度。进程调度的频率很高,一般几十毫秒一次。

作业调度从外存的后备队列中选择一批作业进入内存,为它们建立进程,这些进程被送入就绪队列,进程调度从就绪队列中选出一个进程,并把其状态改为运行态,把CPU分配给它。中级调度是为了提高内存的利用率,系统将那些暂时不能运行的进程挂起来。当内存空间宽松时,通过中级调度选择具备运行条件的进程,将其唤醒。
1)作业调度为进程活动做准备,进程调度使进程正常活动起来,中级调度将暂时不能运行的进程挂起,中级调度处于作业调度和进程调度之间。
2)作业调度次数少,中级调度次数略多,进程调度频率最高。
3)进程调度是最基本的,不可或缺。
进程调度和切换程序是操作系统内核程序。请求调度的事件发生后,才可能运行进程调度程序,调度了新的就绪进程后,才会进行进程间的切换。理论上这三件事情应该顺序执行,但在实际设计中,操作系统内核程序运行时,若某时发生了引起进程调度的因素,则不一定能够马上进行调度与切换。
现代操作系统中,不能进行进程的调度与切换的情况有以下几种:
1)在处理中断的过程中。中断处理过程复杂,在实现上很难做到进程切换,而且中断处理是系统工作的一部分,逻辑上不属于某一进程,不应被剥夺处理机资源。
2)进程在操作系统内核程序临界区中。进入临界区后,需要独占式地访问共享数据,理论上必须加锁,以防止其他并行程序进入,在解锁前不应切换到其他进程运行,以加快该共享数据的释放。
3)其他需要完全屏蔽中断的原子操作过程中。如加锁、解锁、中断现场保护、恢复等原子操作。在原子过程中,连中断都要屏蔽,更不应该进行进程调度与切换。
若在上述过程中发生了引起调度的条件,则不能马上进行调度和切换,应置系统的请求调度标志,直到上述过程结束后才进行相应的调度与切换。
应该进行进程调度与切换的情况如下:
1)发生引起调度条件且当前进程无法继续运行下去时,可以马上进行调度与切换。若操作系统只在这种情况下进行进程调度,则是非剥夺调度。
2)中断处理结束或自陷处理结束后,返回被中断进程的用户态程序执行现场前,若置上请求调度标志,即可马上进行进程调度与切换。若操作系统支持这种情况下的运行调度程序,则实现了剥夺方式的调度。
进程切换往往在调度完成后立刻发生,它要求保存原进程当前切换点的现场信息,恢复被调度进程的现场信息。现场切换时,操作系统内核将原进程的现场信息推入当前进程的内核堆栈来保存它们,并更新堆栈指针。内核完成从新进程的内核栈中装入新进程的现场信息、更新当前运行进程空间指针、重设PC寄存器等相关工作之后,开始运行新的进程。
所谓进程调度方式,是指当某个进程正在处理机上执行时,若有某个更为重要或紧迫的进程需要处理,即有优先权更高的进程进入就绪队列,此时应如何分配处理机。
通常有以下两种进程调度方式:
1)非剥夺调度方式,又称非抢占方式。非剥夺调度方式是指当一个进程正在处理机上执行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在执行的进程继续执行直到该进程完成或发生某种事件而进入阻塞态时,才把处理机分配给更为重要或紧迫的进程。
在非剥夺调度方式下,一旦把CPU分配给一个进程,该进程就会保持CPU直到终止或转换到等待态。这种方式的优点是实现简单、系统开销小,适用于大多数的批处理系统但它不能用于分时系统和大多数的实时系统。
2)剥夺调度方式,又称抢占方式。剥夺调度方式是指当一个进程正在处理机上执行时,若有某个更为重要或紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给这个更为重要或紧迫的进程。
采用剥夺式的调度,对提高系统吞吐率和响应效率都有明显的好处。但“剥夺”不是一种任意性行为,必须遵循一定的原则,主要有优先权、短进程优先和时间片原则等。
不同的调度算法具有不同的特性,在选择调度算法时,必须考虑算法的特性。为了比较处理机调度算法的性能,人们提出了很多评价准则,下面介绍其中主要的几种:
1)CPU利用率。CPU是计算机系统中最重要和昂贵的资源之一,所以应尽可能使CPU保持“忙”状态,使这一资源利用率最高。
2)系统吞吐量。表示单位时间内CPU完成作业的数量。长作业需要消耗较长的处理机时间,因此会降低系统的吞吐量。而对于短作业,它们所需要消耗的处理机时间较短,因此能提高系统的吞吐量。调度算法和方式的不同,也会对系统的吞吐量产生较大的影响。3)周转时间。周转时间是指从作业提交到作业完成所经历的时间,是作业等待、在就绪队列中排队、在处理机上运行及进行输入/输出操作所花费时间的总和。
作业的周转时间可用公式表示如下:
周转时间=作业完成时间-作业提交时间
平均周转时间是指多个作业周转时间的平均值:
平均周转时间=(作业1的周转时间+…+作业 $n$ 的周转时间)/ $n$
带权周转时间是指作业周转时间与作业实际运行时间的比值:
$\text{带权周转时间}=\dfrac{\text{作业周转时间}}{作业实际运行时间}$
平均带权周转时间是指多个作业带权周转时间的平均值:
平均带权周转时间=(作业1的带权周转时间+..+作业 $n$ 的带权周转时间)/ $n$
4)等待时间。等待时间指进程处于等处理机状态的时间之和,等待时间越长,用户满意度越低。处理机调度算法实际上并不影响作业执行或输入/输出操作的时间,只影响作业在就绪队列中等待所花的时间。因此,衡量一个调度算法的优劣,常常只需简单地考察等待时间。
5)响应时间。响应时间指从用户提交请求到系统首次产生响应所用的时间。在交互式系统中,周转时间不可能是最好的评价准则,一般采用响应时间作为衡量调度算法的重要准则之一。从用户角度来看,调度策略应尽量降低响应时间,使响应时间处在用户能接受的范围之内。
要想得到一个满足所有用户和系统要求的算法几乎是不可能的。设计调度程序,一方面要满足特定系统用户的要求(如某些实时和交互进程的快速响应要求),另一方面要考虑系统整体效率(如减少整个系统的进程平均周转时间),同时还要考虑调度算法的开销。
FCFS 调度算法是一种最简单的调度算法,它既可用于作业调度,又可用于进程调度。在作业调度中,算法每次从后备作业队列中选择最先进入该队列的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。
在进程调度中,FCFS 调度算法每次从就绪队列中选择最先进入该队列的进程,将处理机分配给它,使之投入运行,直到完成或因某种原因而阻塞时才释放处理机。
下面通过一个实例来说明FCFS调度算法的性能。假设系统中有4个作业,它们的提交时间分别是8,8.4,8.8,9,运行时间依次是2,1,0.5,0.2,系统采用FCFS调度算法,这组作业的平均等待时间、平均周转时间和平均带权周转时间见表2.2。

FCFS 调度算法属于不可剥夺算法。从表面上看,它对所有作业都是公平的,但若一个长作业先到达系统,就会使后面的许多短作业等待很长时间,因此它不能作为分时系统和实时系统的主要调度策略。但它常被结合在其他调度策略中使用。例如,在使用优先级作为调度策略的系统中,往往对多个具有相同优先级的进程按FCFS原则处理。
FCFS 调度算法的特点是算法简单,但效率低;对长作业比较有利,但对短作业不利(相对SJF和高响应比);有利于CPU繁忙型作业,而不利于I/O繁忙型作业。
短作业(进程)优先调度算法是指对短作业(进程)优先调度的算法。短作业优先(SJF)调度算法从后备队列中选择一个或若干估计运行时间最短的作业,将它们调入内存运行;短进程优先(SPF)调度算法从就绪队列中选择一个估计运行时间最短的进程,将处理机分配给它,使之立即执行,直到完成或发生某事件而阻塞时,才释放处理机。
例如,考虑表2.2中给出的一组作业,若系统采用短作业优先调度算法,其平均等待时间、平均周转时间和平均带权周转时间见表2.3。

SJF调度算法也存在不容忽视的缺点:
1)该算法对长作业不利,由表2.2和表2.3可知,SJF调度算法中长作业的周转时间会增加更严重的是,若有一长作业进入系统的后备队列,由于调度程序总是优先调度那些(E使是后进来的)短作业,将导致长作业长期不被调度(“饥饿”现象,注意区分“死锁”后者是系统环形等待,前者是调度策略问题)。
2)该算法完全未考虑作业的紧迫程度,因而不能保证紧迫性作业会被及时处理。
3)由于作业的长短只是根据用户所提供的估计执行时间而定的,而用户又可能会有意或无意地缩短其作业的估计运行时间,致使该算法不一定能真正做到短作业优先调度。
注意,SJF调度算法的平均等待时间、平均周转时间最少。
优先级调度算法又称优先权调度算法,它既可用于作业调度,又可用于进程调度。该算法中的优先级用于描述作业运行的紧迫程度。
在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最高的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它,使之投入运行。
根据新的更高优先级进程能否抢占正在执行的进程,可将该调度算法分为如下两种:
1)非剥夺式优先级调度算法。当一个进程正在处理机上运行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在运行的进程继续运行,直到由于其自身的原因而主动让出处理机时(任务完成或等待事件),才把处理机分配给更为重要或紧迫的进程。
2)剥夺式优先级调度算法。当一个进程正在处理机上运行时,若有某个更为重要或紧迫的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给更重要或紧迫的进程。
而根据进程创建后其优先级是否可以改变,可以将进程优先级分为以下两种:
1)静态优先级。优先级是在创建进程时确定的,且在进程的整个运行期间保持不变。确定静态优先级的主要依据有进程类型、进程对资源的要求、用户要求。
2)动态优先级。在进程运行过程中,根据进程情况的变化动态调整优先级。动态调整优先级的主要依据有进程占有CPU时间的长短、就绪进程等待CPU时间的长短。
一般来说,进程优先级的设置可以参照以下原则:
1)系统进程>用户进程。系统进程作为系统的管理者,理应拥有更高的优先级。
2)交互型进程>非交互型进程(或前台进程>后台进程)。大家平时在使用手机时,在前台运行的正在和你交互的进程应该更快速地响应你,因此自然需要被优先处理,即要有更高的优先级。
3)I/O型进程>计算型进程。所谓IO型进程,是指那些会频繁使用IO设备的进程,而计算型进程是那些频繁使用CPU的进程(很少使用I/O设备)。我们知道,IO设备(如打印机)的处理速度要比CPU慢得多,因此若将IO型进程的优先级设置得更高,就更有可能让IO设备尽早开始工作,进而提升系统的整体效率。
高响应比优先调度算法主要用于作业调度,是对FCFS调度算法和SJF调度算法的一种综合平衡,同时考虑了每个作业的等待时间和估计的运行时间。在每次进行作业调度时,先计算后备作业队列中每个作业的响应比,从中选出响应比最高的作业投入运行。
响应比的变化规律可描述为
$\text{响应比}R_p=\dfrac{\text{等待时间}+\text{要求服务时间}}{\text{要求服务时间}}$
根据公式可知:
1)作业的等待时间相同时,要求服务时间越短,响应比越高,有利于短作业。
2)要求服务时间相同时,作业的响应比由其等待时间决定,等待时间越长,其响应比越高,因而它实现的是先来先服务。
3)对于长作业,作业的响应比可以随等待时间的增加而提高,等待时间足够长时,其响应比便可升到很高,从而也可获得处理机。因此,克服了饥饿状态,兼顾了长作业。
时间片轮转调度算法主要适用于分时系统。在这种算法中,系统将所有就绪进程按到达时间的先后次序排成一个队列,进程调度程序总是选择就绪队列中的第一个进程执行,即先来先服务的原则,但仅能运行一个时间片,如100ms。在使用完一个时间片后,即使进程并未完成其运行,它也必须释放出(被剥夺)处理机给下一个就绪的进程,而被剥夺的进程返回到就绪队列的末尾重新排队,等候再次运行。
在时间片轮转调度算法中,时间片的大小对系统性能的影响很大。若时间片足够大,以至于所有进程都能在一个时间片内执行完毕,则时间片轮转调度算法就退化为先来先服务调度算法。若时间片很小,则处理机将在进程间过于频繁地切换,使处理机的开销增大,而真正用于运行用户进程的时间将减少。因此,时间片的大小应选择适当。
时间片的长短通常由以下因素确定:系统的响应时间、就绪队列中的进程数目和系统的处理能力。
多级反馈队列调度算法是时间片轮转调度算法和优先级调度算法的综合与发展,如图2.7所示。通过动态调整进程优先级和时间片大小,多级反馈队列调度算法可以兼顾多方面的系统目标。例如,为提高系统吞吐量和缩短平均周转时间而照顾短进程;为获得较好的IO设备利用率和缩短响应时间而照顾IO型进程;同时,也不必事先估计进程的执行时间。

多级反馈队列调度算法的实现思想如下:
1)设置多个就绪队列,并为各个队列赋予不同的优先级,第1级队列的优先级最高,第2级队列次之,其余队列的优先级逐次降低。
2)赋予各个队列中进程执行时间片的大小各不相同。在优先级越高的队列中,每个进程的运行时间片越小。例如,第2级队列的时间片要比第1级队列的时间片长1倍……第 $i$ +1级队列的时间片要比第 $i$ 级队列的时间片长1倍。
3)一个新进程进入内存后,首先将它放入第1级队列的末尾,按FCFS原则排队等待调度当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;若它在一个时间片结束时尚未完成,调度程序便将该进程转入第2级队列的末尾,再同样按FCFS原则等待调度执行;若它在第2级队列中运行一个时间片后仍未完成,再以同样的方法放入第3级队列……如此下去,当一个长进程从第1级队列依次降到第 $n$ 级队列后,在第 $n$ 级队列中便采用时间片轮转的方式运行。
4)仅当第1级队列为空时,调度程序才调度2级队列中的进程运行;仅当第1~( $i$ -1)级队列均为空时,才会调度第 $i$ 级队列中的进程运行。若处理机正在执行第 $i$ 级队列中的某进程,这时又有新进程进入优先级较高的队列〔第1~( $i$ -1)中的任何一个队列],则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回第i级队列的末尾,把处理机分配给新到的更高优先级的进程。
多级反馈队列的优势有以下几点:
1)终端型作业用户:短作业优先。
2)短批处理作业用户:周转时间较短。
3)长批处理作业用户:经过前面几个队列得到部分执行,不会长期得不到处理。
本节开头提出的问题的参考答案如下。
1)为什么要进行处理机调度?
若没有处理机调度,同意味着要等到当前运行的进程执行完毕后,下一个进程才能执行,而实际情况中,进程时常需要等待一些外部设备的输入,而外部设备的速度与处理机相比是非常缓慢的,若让处理机总是等待外部设备,则对处理机的资源是极大的浪费。而引进处理机调度后,可在运行进程等待外部设备时,把处理机调度给其他进程,从而提高处理机的利用率。用一句简单的话说,就是为了合理地处理计算机的软/硬件资源。
2)调度算法有哪几种?结合第1章学习的分时操作系统和实时操作系统,思考有没有哪种调度算法比较适合这两种操作系统。
本节介绍的调度算法有先来先服务调度算法、短作业优先调度算法、优先级调度算法、高响应比优先调度算法、时间片轮转调度算法、多级反馈队列调度算法6种。
先来先服务算法和短作业优先算法无法保证及时地接收和处理问题,因此尤法休证仕规疋时时间间隔内响应每个用户的需求,也同样无法达到实时操作系统的及的性而水。优兀级A度开仫按照任务的优先级进行调度,对于更紧急的任务给予更高的优先级,适合实时操作系统。
高响应比优先调度算法、时间片轮转调度算法、多级反馈队列调度算法都能保证每个任务在一定时间内分配到时间片,并轮流占用CPU,适合分时操作系统。
本节主要介绍了处理机调度的概念。操作系统主要管理处理机、内存、文件、设备几种资源,只要对资源的请求大于资源本身的数量,就会涉及调度。例如,在单处理机系统中,处理机只有一个,而请求服务的进程却有多个,所以就有处理机调度的概念出现。而出现调度的概念后,又有了一个问题,即如何调度、应该满足谁、应该让谁等待,这是调度算法所回答的问题;而应该满足谁、应该让谁等待,要遵循一定的准则,即调度的准则。调度这一概念贯穿于操作系统的始终,读者在接下来的学习中,将接触到几种资源的调度问题和相应的调度算法。将它们与处理机调度的内容相对比,将会发现它们有异曲同工之妙。
在学习本节时,请读者思考以下问题:
1)为什么要引入进程同步的概念?
2)不同的进程之间会存在什么关系?
3)当单纯用本节介绍的方法解决这些问题时会遇到什么新的问题吗?
用PV操作解决进程之间的同步互斥问题是这一节的重点,考试已经多次考查过这一内容,读者务必多加练习,掌握好求解问题的方法。
在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。为了协调进程之间的相互制约关系,引入了进程同步的概念。下面举一个简单的例于米帮大豕理解赵个概念。例如,让系统计算1+2x3,假设系统产生两个进程:一个是加法进程,一个是乘法进程。要让计算结果是正确的,一定要让加法进程发生在乘法进程之后,但实际上操作系统具有异步性,若不加以制约,加法进程发生在乘法进程之前是绝对有可能的,因此要制定一定的机制去约束加法进程,让它在乘法进程完成之后才发生,而这种机制就是本节要讨论的内容。
虽然多个进程可以共享系统中的各种资源,但其中许多资源一次只能为一个进程所用,我们将一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如打印机等。此外,还有许多变量、数据等都可以被若干进程共享,也属于临界资源。
对临界资源的访问,必须互斥地进行,在每个进程中,访问临界资源的那段代码称为临界区。为了保证临界资源的正确使用,可把临界资源的访问过程分成4个部分:
1)进入区。为了进入临界区使用临界资源,在进入区要检查可否进入临界区,若能进入临界区,则应设置正在访问临界区的标志,以阻止其他进程同时进入临界区。
2)临界区。进程中访问临界资源的那段代码,又称临界段。
3)退出区。将正在访问临界区的标志清除。
4)剩余区。代码中的其余部分。
1 | do{ |
同步亦称直接制约关系,是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系源于它们之间的相互合作。
例如,输入进程A通过单缓冲向进程B提供数据。当该缓冲区空时,进程B不能获得所需数据而阻塞,一旦进程A将数据送入缓冲区,进程B就被唤醒。反之,当缓冲区满时,进程A被阻塞,仅当进程B取走缓冲数据时,才唤醒进程A。
互斥也称间接制约关系。当一个进程进入临界区使用临界资源时,另一个进程必须等待,当占用临界资源的进程退出临界区后,另一进程才允许去访问此临界资源。
例如,在仅有一台打印机的系统中,有两个进程A和进程B,若进程A需要打印时,系统已将打印机分配给进程B,则进程A必须阻塞。一旦进程B将打印机释放,系统便将进程唤醒,并将其由阻塞态变为就绪态。
为禁止两个进程同时进入临界区,同步机制应遵循以下准则:
1))空闲让进。临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。
2)忙则等待。当已有进程进入临界区时,其他试图进入临界区的进程必须等待。
3)有限等待。对请求访问的进程,应保证能在有限时间内进入临界区。
4)让权等待。当进程不能进入临界区时,应立即释放处理器,防止进程忙等待。
在进入区设置并检查一些标志来标明是否有进程在临界区中,若已有进程在临界区,则在进入区通过循环检查进行等待,进程离开临界区后则在退出区修改标志。
1)算法一:单标志法。该算法设置一个公用整型变量turn,用于指示被允许进入临界区的进程编号,即若turn=0,则允许P进程进入临界区。该算法可确保每次只允许一个进程进入临界区。但两个进程必须交替进入临界区,若某个进程不再进入临界区,则另一个进程也将无法进入临界区(违背“空闲让进”)。这样很容易造成资源利用不充分。若P顺利进入临界区并从临界区离开,则此时临界区是空闲的,但P并没有进入临界区的打算,turn=1一直成立,P。就无法再次进入临界区(一直被while死循环困住)。

2)算法二:双标志法先检查。该算法的基本思想是在每个进程访问临界区资源之前,先查看临界资源是否正被访问,若正被访问,该进程需等待;否则,进程才进入自己的临界区。为此,设置一个数据flag[ $i$ ],如第i个元素值为FALSE,表示 $P_i$ 进程未进入临界区,值为TRUE,表示P进程进入临界区。

优点:不用交替进入,可连续使用;缺点: $P_i$ 和 $P_j$ ,可能同时进入临界区。按序列①② ${\textstyle\unicode{x2462}}$ ${\textstyle\unicode{x2463}}$ 执行时,会同时进入临界区(违背“忙则等待”)。即在检查对方的 flag 后和切换自己的flag前有一段时间,结果都检查通过。这里的问题出在检查和修改操作不能一次进行。
3)算法三:双标志法后检查。算法二先检测对方的进程状态标志,再置自己的标志,由在检测和放置中可插入另一个进程到达时的检测操作,会造成两个进程在分别检测后同时进入临界区。为此,算法三先将自己的标志设置为 TRUE,再检测对方的状态标志,若对方标志为TRUE,则进程等待;否则进入临界区。

两个进程几乎同时都想进入临界区时,它们分别将自己的标志值 flag 设置为 TRUE,并且同时检测对方的状态(执行while语句),发现对方也要进入临界区时,双方互相谦让,结果谁也进不了临界区,从而导致“饥饿”现象。
4)算法四:Peterson’s Algorithm。为了防止两个进程为进入临界区而无限期等待,又设置变量 turn,每个进程在先设置自己的标志后再设置turn标志。这时,再同时检测另一个进程状态标志和不允许进入标志,以便保证两个进程同时要求进入临界区时,只允许个进程进入临界区。

具体如下:考虑进程P;,一旦设置flag[ $i$ ] = true,就表示它想要进入临界区,同时turn =j,此时若进程P,已在临界区中,符合进程 $P_i$ 中的while循环条件,则 $P_i$ 不能进入临界区。若 $P_j$ 不想要进入临界区,即 flag[ $j$ ] = false,循环条件不符合,则 $P_i$ 可以顺利进入,反之亦然。本算法的基本思想是算法一和算法三的结合。利用flag解决临界资源的互斥访问,而利用turn解决“饥饿”现象。
理解Peterson’s Algorithm的最好方法就是手动模拟。
理解本节介绍的硬件实现,对学习后面的信号量很有帮助。计算机提供了特殊的硬件指令,允许对一个字中的内容进行检测和修正,或对两个字的内容进行交换等。通过硬件支持实现临界段问题的方法称为低级方法,或称元方法。
(1)中断屏蔽方法
当一个进程正在使用处理机执行它的临界区代码时,防止其他进程进入其临界区进行访问的最简方法是,禁止一切中断发生,或称之为屏蔽中断、关中断。因为CPU只在发生中断时引起进程切换,因此屏蔽中断能够保证当前运行的进程让临界区代码顺利地执行完,进而保证互斥的正确实现,然后执行开中断。其典型模式为

这种方法限制了处理机交替执行程序的能力,因此执行的效率会明显降低。对内核来说,在它执行更新变量或列表的几条指令期间,关中断是很方便的,但将关中断的权力交给用户则很不明智,若一个进程关中断后不再开中断,则系统可能会因此终止。
(2)硬件指令方法
TestAndSet 指令:这条指令是原子操作,即执行该代码时不允许被中断。其功能是读出指定标志后把该标志设置为真。指令的功能描述如下:

可以为每个临界资源设置一个共享布尔变量lock,表示资源的两种状态: true表示正被占用,初值为false。在进程访问临界资源之前,利用TestAndSet检查和修改标志lock;若有进程在临界区,则重复检查,直到进程退出。利用该指令实现进程互斥的算法描述如下:

Swap指令:该指令的功能是交换两个字(字节)的内容。其功能描述如下:

注意:以上对TestAndSet和Swap指令的描述仅是功能实现,而并非软件实现的定义。事实上,它们是由硬件逻辑直接实现的,不会被中断。
应为每个临界资源设置一个共享布尔变量lock,初值为false;在每个进程中再设置一个局部布尔变量 key,用于与lock交换信息。在进入临界区前,先利用Swap 指令交换lock 与 key的内容,然后检查key 的状态;有进程在临界区时,重复交换和检查过程,直到进程退出。利用Swap指令实现进程互斥的算法描述如下:

硬件方法的优点:适用于任意数目的进程,而不管是单处理机还是多处理机;简单、容易验证其正确性。可以支持进程内有多个临界区,只需为每个临界区设立一个布尔变量。
硬件方法的缺点:进程等待进入临界区时要耗费处理机时间,不能实现让权等待。从等待进程中随机选择一个进入临界区,有的进程可能一直选不上,从而导致“饥饿”现象。
无论是软件实现方法还是硬件实现方法,读者只需理解它的执行过程即可,关键是软件实现方法。实际练习和考试中很少让读者写出某种软件和硬件实现方法,因此读者并不需要默写或记忆。以上的代码实现与我们平时在编译器上写的代码意义不同,以上的代码实现是为了表述进程实现同步和互斥的过程,并不是说计算机内部实现同步互斥的就是这些代码。
信号量机制是一种功能较强的机制,可用来解决互斥与同步问题,它只能被两个标准的原语wait(S)和 signal(S)访问,也可记为“P操作”和“V操作”。
原语是指完成某种功能且不被分割、不被中断执行的操作序列,通常可由硬件来实现。例如,前述的Test-and-Set和Swap指令就是由硬件实现的原子操作。原语功能的不被中断执行特性在单处理机上可由软件通过屏蔽中断方法实现。
原语之所以不能被中断执行,是因为原语对变量的操作过程若被打断,可能会去运行另一个对同一变量的操作过程,从而出现临界段问题。若能够找到一种解决临界段问题的元方法,就可以实现对共享变量操作的原子性。
整型信号量被定义为一个用于表示资源数目的整型量S,wait和 signal操作可描述为

wait 操作中,只要信号量S≤0,就会不断地测试。因此,该机制并未遵循“让权等待”的准则,而是使进程处于“忙等”的状态。
记录型信号量是不存在“忙等”现象的进程同步机制。除需要一个用于代表资源数目的整型变量value 外,再增加一个进程链表L,用于链接所有等待该资源的进程。记录型信号量得名于采用了记录型的数据结构。记录型信号量可描述为

相应的 wait(S)和 signal(S)的操作如下:

wait操作,S.value–表示进程请求一个该类资源,当S.value <0时,表示该类资源已分配完毕,因此进程应调用block 原语,进行自我阻塞,放弃处理机,并插入该类资源的等待队列S.L,可见该机制遵循了“让权等待”的准则。

signal操作,表示进程释放一个资源,使系统中可供分配的该类资源数增1,因此有S.value ++。若加1后仍是S.value≤0,则表示在S.L中仍有等待该资源的进程被阻塞,因此还应调用wakeup原语,将S.L中的第一个等待进程唤醒。
信号量机制能用于解决进程间的各种同步问题。设S为实现进程 $P_1$ , $P_2$ 同步的公共信号量,初值为0。进程 $P_2$ 中的语句y要使用进程 $P_1$ 中语句x的运行结果,所以只有当语句x执行完成之后语句y 才可以执行。其实现进程同步的算法如下:

若 $P_2$ 先执行到P(S)时,S为0,执行Р操作会把进程 $P_2$ 阻塞,并放入阻塞队列;当进程 $P_1$ 中的x执行完后,执行V操作,把 $P_2$ 从阻塞队列中放回就绪队列,当 $P_2$ 得到处理机时,就得以继续执行。
信号量机制也能很方便地解决进程互斥问题。设S为实现进程 $P_1$ , $P_2$ 互斥的信号量,由于每次只允许一个进程进入临界区﹐所以S的初值应为1(即可用资源数为1)。只需把临界区置于P(S)和V(S)之间,即可实现两个进程对临界资源的互斥访问。其算法如下:

当没有进程在临界区时,任意一个进程要进入临界区,就要执行Р操作,把S的值减为0,然后进入临界区;当有进程存在于临界区时,S的值为0,再有进程要进入临界区,执行Р操作时将会被阻塞,直至在临界区中的进程退出,这样便实现了临界区的互斥。
互斥是不同进程对同一信号量进行P,V操作实现的,一个进程成功对信号量执行了Р操作后进入临界区,并在退出临界区后,由该进程本身对该信号量执行V操作,表示当前没有进程进入临界区,可以让其他进程进入。
下面简单总结一下PV操作在同步互斥中的应用:在同步问题中,若某个行为要用到某种资源,则在这个行为前面Р这种资源一下;若某个行为会提供某种资源,则在这个行为后面V这种资源一下。在互斥问题中,P, V操作要紧夹使用互斥资源的那个行为,中间不能有其他冗余代码。

信号量也可用来描述程序之间或语句之间的前驱关系。图2.8给出了一个前驱图,其中 $S_1$ , $S_2$ , $S_3$ ,… , $S_6$ 是最简单的程序段(只有一条语句)。为使各程序段能正确执行,应设置若干初始值为“0”的信号量。例如,为保证 $S_1$ → $S_2$ , $S_1$ → $S_3$ 的前驱关系,应分别设置信号量al, a2。同样,为保证 $S_2$ → $S_4$ , $S_2$ → $S_5$ , $S_5$ → $S_6$ , $S_4$ → $S_6$ , $S_5$ → $S_6$ ,应设置信号量b1, b2,c, d,e。

1)关系分析。找出问题中的进程数,并分析它们之间的同步和互斥关系。同步、互斥、前驱关系直接按照上面例子中的经典范式改写。
2)整理思路。找出解决问题的关键点,并根据做过的题目找出求解的思路。根据进程的操作流程确定Р操作、V操作的大致顺序。
3)设置信号量。根据上面的两步,设置需要的信号量,确定初值,完善整理。
这是一个比较直观的同步问题,以 $S_2$ 为例,它是 $S_1$ 的后继,所以要用到 $S_1$ 的资源,在前面的简单总结中我们说过,在同步问题中,要用到某种资源,就要在行为(题中统一抽象成L)前面Р这种资源一下。 $S_2$ 是 $S_4$ , $S_5$ 的前驱,给 $S_4$ , $S_5$ ,提供资源,所以要在L行为后面V由 $S_4$ 和 $S_5$ 代表的资源一下。
在信号量机制中,每个要访问临界资源的进程都必须自备同步的PV操作,大量分散的同步操作给系统管理带来了麻烦,且容易因同步操作不当而导致系统死锁。于是,便产生了一种新的进程同步工具-管程。管程的特性保证了进程互斥,无须程序员自己实现互斥,从而降低了死锁发生的可能性。同时管程提供了条件变量,可以让程序员灵活地实现进程同步。
系统中的各种硬件资源和软件资源,均可用数据结构抽象地描述其资源特性,即用少量信息和对资源所执行的操作来表征该资源,而忽略它们的内部结构和实现细节。
利用共享数据结构抽象地表示系统中的共享资源,而把对该数据结构实施的操作定义为一组过程。进程对共享资源的申请、释放等操作,都通过这组过程来实现,这组过程还可以根据资源情况,或接受或阻塞进程的访问,确保每次仅有一个进程使用共享资源,这样就可以统一管理对共享资源的所有访问,实现进程互斥。这个代表共享资源的数据结构,以及由对该共享数据结构实施操作的一组过程所组成的资源管理程序,称为管程( monitor )。管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据。
由上述定义可知,管程由4部分组成:
①管程的名称;
${\textstyle\unicode{x2461}}$ 局部于管程内部的共享结构数据说明;
${\textstyle\unicode{x2462}}$对该数据结构进行操作的一组过程(或函数);
${\textstyle\unicode{x2463}}$ 对局部于管程内部的共享数据设置初始值的语句。
管程的定义描述举例如下:

熟悉面向对象程序设计的读者看到管程的组成后,会立即联想到管程很像一个类(class)。
1)管程把对共享资源的操作封装起来,管程内的共享数据结构只能被管程内的过程所访问。一个进程只有通过调用管程内的过程才能进入管程访问共享资源。对于上例,外部进程只能通过调用take_away()过程来申请一个资源;归还资源也一样。
2)每次仅允许一个进程进入管程,从而实现进程互斥。若多个进程同时调用take_away(),give_back(),则只有某个进程运行完它调用的过程后,下个进程才能开始运行它调用的过程。也就是说,各个进程只能串行执行管程内的过程,这一特性保证了进程“互斥”访问共享数据结构S。
当一个进程进入管程后被阻塞,直到阻塞的原因解除时,在此期间,如果该进程不释放管程,那么其他进程无法进入管程。为此,将阻塞原因定义为条件变量condition。通常,一个进程被阻塞的原因可以有多个,因此在管程中设置了多个条件变量。每个条件变量保存了一个等待队列,用于记录因该条件变量而阻塞的所有进程,对条件变量只能进行两种操作,即 wait和 signal。
x.wait:当x对应的条件不满足时,正在调用管程的进程调用x.wait将自己插入x条件的等待队列,并释放管程。此时其他进程可以使用该管程。
x.signal: x对应的条件发生了变化,则调用x.signal,唤醒一个因x条件而阻塞的进程。下面给出条件变量的定义和使用:

条件变量和信号量的比较:
相似点:条件变量的wait/signal操作类似于信号量的P/V操作,可以实现进程的阻塞/唤醒。
不同点:条件变量是“没有值”的,仅实现了“排队等待”功能;而信号量是“有值”的,信号量的值反映了剩余资源数,而在管程中,剩余资源数用共享数据结构记录。
问题描述:一组生产者进程和一组消费者进程共享一个初始为空、大小为n的缓冲区,只有缓冲区没满时,生产者才能把消息放入缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。
问题分析:
1)关系分析。生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者又是个相互协作的关系,只有生产者生产之后,消费者才能消费,它们也是同步关系。
2)整理思路。这里比较简单,只有生产者和消费者两个进程,正好是这两个进程存在着互斥关系和同步关系。那么需要解决的是互斥和同步PV操作的位置。
3)信号量设置。信号量mutex作为互斥信号量,用于控制互斥访问缓冲池,互斥信号量初值为1;信号量full用于记录当前缓冲池中的“满”缓冲区数,初值为0。信号量empty用于记录当前缓冲池中的“空”缓冲区数,初值为 $n$ 。
我们对同步互斥问题的介绍是一个循序渐进的过程。上面介绍了一个同步问题的例子和一个互斥问题的例子,下面来看生产者-消费者问题的例子是什么样的。
生产者-消费者进程的描述如下:

该类问题要注意对缓冲区大小为n的处理,当缓冲区中有空时,便可对empty变量执行Р操作,一旦取走一个产品便要执行V操作以释放空闲区。对empty和 full变量的Р操作必须放在对mutex 的P操作之前。若生产者进程先执行P(mutex),然后执行P(empty),消费者执行P(mutex),然后执行 P(full),这样可不可以﹖答案是否定的。设想生产者进程已将缓冲区放满,消费者进程并没有取产品,即 empty =0,当下次仍然是生产者进程运行时,它先执行P(mutex)封锁信号量,再执行P(empty)时将被阻塞,希望消费者取出产品后将其唤醒。轮到消费者进程运行时,它先执行P(mutex),然而由于生产者进程已经封锁mutex信号量,消费者进程也会被阻塞,这样一来生产者、消费者进程都将阻塞,都指望对方唤醒自己,因此陷入了无休止的等待。同理,若消费者进程已将缓冲区取空,即 full = 0,下次若还是消费者先运行,也会出现类似的死锁。不过生产者释放信号量时,mutex, full 先释放哪一个无所谓,消费者先释放mutex 或empty都可以。
根据对同步互斥问题的简单总结,我们发现,其实生产者-消费者问题只是一个同步互斥问题的综合而已。
下面再看一个较为复杂的生产者-消费者问题。
问题描述:桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。只有盘子为空时,爸爸或妈妈才可向盘子中放一个水果;仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出。
问题分析:
1)关系分析。这里的关系要稍复杂一些。由每次只能向其中放入一只水果可知,爸爸和妈妈是互斥关系。爸爸和女儿、妈妈和儿子是同步关系,而且这两对进程必须连起来,儿子和女儿之间没有互斥和同步关系,因为他们是选择条件执行,不可能并发,如图2.9所示。
2)整理思路。这里有4个进程,实际上可抽象为两个生产者和两个消费者被连接到大小为1的缓冲区上。
3)信号量设置。首先将信号量plate设置互斥信号量,表示是否允许向盘子放入水果,初值为1表示允许放入,且只允许放入一个。信号量apple表示盘子中是否有苹果,初值为0表示盘子为空,不许取,apple = 1表示可以取。信号量orange表示盘子中是否有橘子,初值为0表示盘子为空,不许取,orange =1表示可以取。

解决该问题的代码如下:

进程间的关系如图2.9所示。dad()和 daughter()、mom()和 son()必须连续执行,正因为如此,也只能在女儿拿走苹果后或儿子拿走橘子后才能释放盘子,即V(plate)操作。
问题描述:有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。
问题分析:
1)关系分析。由题目分析读者和写者是互斥的,写者和写者也是互斥的,而读者和读者不存在互斥问题。
2)整理思路。两个进程,即读者和写者。写者是比较简单的,它和任何进程互斥,用互斥信号量的Р操作、V操作即可解决。读者的问题比较复杂,它必须在实现与写者互斥的同时,实现与其他读者的同步,因此简单的一对Р操作、V操作是无法解决问题的。这里用到了一个计数器,用它来判断当前是否有读者读文件。当有读者时,写者是无法写文件的,此时读者会一直占用文件,当没有读者时,写者才可以写文件。同时,这里不同读者对计数器的访问也应该是互斥的。
3)信号量设置。首先设置信号量count为计数器,用于记录当前读者的数量,初值为0;设置mutex为互斥信号量,用于保护更新count变量时的互斥;设置互斥信号量rw,用于保证读者和写者的互斥访问。
代码如下:

在上面的算法中,读进程是优先的,即当存在读进程时,写操作将被延迟,且只要有一个读进程活跃,随后而来的读进程都将被允许访问文件。这样的方式会导致写进程可能长时间等待,且存在写进程“饿死”的情况。
若希望写进程优先,即当有读进程正在读共享文件时,有写进程请求访问,这时应禁止后续读进程的请求,等到已在共享文件的读进程执行完毕,立即让写进程执行,只有在无写进程执行的情况下才允许读进程再次运行。为此,增加一个信号量并在上面程序的 writer()和reader()函数中各增加一对PV操作,就可以得到写进程优先的解决程序。

这里的写进程优先是相对而言的,有些书上把这个算法称为读写公平法,即读写进程具有-一样的优先级。当一个写进程访问文件时,若先有一些读进程要求访问文件,后有另一个写进程要求访问文件,则当前访问文件的进程结束对文件的写操作时,会是一个读进程而不是一个写进程占用文件(在信号量w的阻塞队列上,因为读进程先来,因此排在阻塞队列队首,而V操作唤醒进程时唤醒的是队首进程),所以说这里的写优先是相对的,想要了解如何做到真正写者优先,
可参考其他相关资料。
读者-写者问题有一个关键的特征,即有一个互斥访问的计数器count,因此遇到一个不太好解决的同步互斥问题时,要想一想用互斥访问的计数器count能否解决问题。

问题描述:一张圆桌边上坐着5名哲学家,每两名哲学家之间的桌上摆一根筷子,两根筷子中间是一碗米饭,如图2.10所示。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。若筷子已在他人手上,则需要等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,进餐完毕后,放下筷子继续思考。
问题分析:
1)关系分析。5名哲学家与左右邻居对其中间筷子的访问是互斥关系。
2)整理思路。显然,这里有5个进程。本题的关键是如何让一名哲学家拿到左右两根筷子而不造成死锁或饥饿现象。解决方法有两个:一是让他们同时拿两根筷子;二是对每名哲学家的动作制定规则,避免饥饿或死锁现象的发生。
3)信号量设置。定义互斥信号量数组chopstick[ 5 ]={1,1,1,1,1},用于对5个筷子的互斥访问。哲学家按顺序编号为0~4,哲学家i左边筷子的编号为i,哲学家右边筷子的编号为(i +1)%5。

该算法存在以下问题:当5名哲学家都想要进餐并分别拿起左边的筷子时(都恰好执行完wait(chopstick[ i]);)筷子已被拿光,等到他们再想拿右边的筷子时(执行wait(chopstick[(i + 1)%5]);)就全被阻塞,因此出现了死锁。
为防止死锁发生,可对哲学家进程施加一些限制条件,比如至多允许4名哲学家同时进餐;仅当一名哲学家左右两边的筷子都可用时,才允许他抓起筷子;对哲学家顺序编号,要求奇数号哲学家先拿左边的筷子,然后拿右边的筷子,而偶数号哲学家刚好相反。
制定的正确规则如下:假设采用第二种方法,当一名哲学家左右两边的筷子都可用时,才允许他抓起筷子。

此外,还可采用AND型信号量机制来解决哲学家进餐问题,有兴趣的读者可以查阅相关资料,自行思考。
熟悉ACM或有过相关训练的读者都应知道贪心算法,哲学家进餐问题的思想其实与贪心算法的思想截然相反,贪心算法强调争取眼前认为最好的,而不考虑后续会有什么后果。若哲学家进餐问题用贪心算法来解决,即只要眼前有筷子能拿起就拿起的话,就会出现死锁。然而,若不仅考虑眼前的一步,而且考虑下一步,即不因为有筷子能拿起就拿起,而考虑能不能一次拿起两根筷子才做决定的话,就会避免死锁问题,这就是哲学家进餐问题的思维精髓。
大部分练习题和真题用消费者-生产者模型或读者-写者问题就能解决,但对于哲学家进餐问题和吸烟者问题仍然要熟悉。考研复习的关键在于反复多次和全面,“偷工减料”是要吃亏的。
问题描述:假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放到桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者一个信号告诉已完成,此时供应者就会将另外两种材料放到桌上,如此重复(让三个抽烟者轮流地抽烟)。
问题分析:
1)关系分析。供应者与三个抽烟者分别是同步关系。由于供应者无法同时满足两个或以上的抽烟者,三个抽烟者对抽烟这个动作互斥(或由三个抽烟者轮流抽烟得知)。
2)整理思路。显然这里有4个进程。供应者作为生产者向三个抽烟者提供材料。
3)信号量设置。信号量offer1, offer2, offer3分别表示烟草和纸组合的资源、烟草和胶水组合的资源、纸和胶水组合的资源。信号量finish用于互斥进行抽烟动作。
代码如下:

本节开头提出的问题的参考答案如下。
在多道程序共同执行的条件下,进程与进程是并发执行的,不同进程之间存在不同的相互制约关系。为了协调进程之间的相互制约关系,引入了进程同步的概念。
进程之间存在同步与互斥的制约关系。
同步是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而等待、传递信息所产生的制约关系。
互斥是指当一个进程进入临界区使用临界资源时,另一个进程必须等待,当占用临界资源的进程退出临界区后,另一进程才允许去访问此临界资源。
当两个或两个以上的进程在执行过程中,因占有一些资源而又需要对方的资源时,会因为争夺资源而造成一种互相等待的现象,若无外力作用,它们都将无法推进下去。这种现象称为死锁,具体介绍和解决方案请参考下一节。
在学习本节时,请读者思考以下问题:
1)为什么会产生死锁?产生死锁有什么条件?
2)有什么办法可以解决死锁问题?
学完本节,读者应了解死锁的由来、产生条件及基本解决方法,区分死锁的避免和死锁的预防。
在多道程序系统中,由于多个进程的并发执行,改善了系统资源的利用率并提高了系统的处理能力。然而,多个进程的并发执行也带来了新的问题–死锁。所谓死锁,是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
下面通过一些实例来说明死锁现象。
先看生活中的一个实例。在一条河上有一座桥,桥面很窄,只能容纳一辆汽车通行。若有两辆汽车分别从桥的左右两端驶上该桥,则会出现下述冲突情况:此时,左边的汽车占有桥面左边的一段,要想过桥还需等待右边的汽车让出桥面右边的一段;右边的汽车占有桥面右边的一段,要想过桥还需等待左边的汽车让出桥面左边的一段。此时,若左右两边的汽车都只能向前行驶,则两辆汽车都无法过桥。
在计算机系统中也存在类似的情况。例如,某计算机系统中只有一台打印机和一台输入设备,进程 $P_1$ 正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程 $P_2$ 所占用,而 $P_2$ 在未释放打印机之前,又提出请求使用正被 $P_1$ 占用的输入设备。这样,两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。
进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 $P_1$ , $P_2$ 分别保持了资源 $R_1$ , $R_2$ ,而进程 $P_1$ 申请资源 $R_2$ 、进程 $P_2$ 申请资源 $R_1$ 时,两者都会因为所需资源被占用而阻塞。
信号量使用不当也会造成死锁。进程间彼此相互等待对方发来的消息,也会使得这些进程间无法继续向前推进。例如,进程A等待进程B发的消息,进程B又在等待进程A发的消息,可以看出进程A和B不是因为竞争同一资源,而是在等待对方的资源导致死锁。
产生死锁必须同时满足以下4个条件,只要其中任意一个条件不成立,死锁就不会发生。
互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
不剥夺条件:进程所获得的资源在未使用完之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
请求并保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
循环等待条件:存在一种进程资源的循环等待链,链中每个进程已获得的资源同时被链中下一个进程所请求。即存在一个处于等待态的进程集合{ $P_1$ , $P_2$ ,…, $P_n$ },其中P,等待的资源被 $P_{i+1}$ ( $i$ =0,1,… , $n$ -1)占有, $P_n$ 等待的资源被 $P_0$ 占有,如图2.11所示。
直观上看,循环等待条件似乎和死锁的定义一样,其实不然。按死锁定义构成等待环所要求的条件更严,它要求 $P_i$ 等待的资源必须由 $P_{i+1}$ 来满足,而循环等待条件则无此限制。例如,系统中有两台输出设备, $P_0$ 占有一台, $P_K$ 占有另一台,且K不属于集合{0,1,…, n}。 $P_n$ 等待一台输出设备,它可从 $P_0$ 获得,也可能从 $P_K$ 获得。因此,虽然 $P_n$ , $P_0$ 和其他一些进程形成了循环等待圈,但 $P_K$ 不在圈内,若 $P_K$ 释放了输出设备,则可打破循环等待,如图2.12所示。因此循环等待只是死锁的必要条件。

资源分配图含圈而系统又不一定有死锁的原因是,同类资源数大于1。但若系统中每类资源都只有一个资源,则资源分配图含圈就变成了系统出现死锁的充分必要条件。
要注意区分不剥夺条件与请求并保持条件。下面用一个简单的例子进行说明:若你手上拿着一个苹果(即便你不打算吃),别人不能把你手上的苹果拿走,则这就是不剥夺条件;若你左手拿着一个苹果,允许你右手再去拿一个苹果,则这就是请求并保持条件。
为使系统不发生死锁,必须设法破坏产生死锁的4个必要条件之一,或允许死锁产生,但当死锁发生时能检测出死锁,并有能力实现恢复。
设置某些限制条件,破坏产生死锁的4个必要条件中的一个或几个,以防止发生死锁。
在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁。
无须采取任何限制性措施,允许进程在运行过程中发生死锁。通过系统的检测机构及时地检测出死锁的发生,然后采取某种措施解除死锁。
预防死锁和避免死锁都属于事先预防策略,预防死锁的限制条件比较严格,实现起来较为简单,但往往导致系统的效率低,资源利用率低;避免死锁的限制条件相对宽松,资源分配后需要通过算法来判断是否进入不安全状态,实现起来较为复杂。
死锁的几种处理策略的比较见表2.4。

防止死锁的发生只需破坏死锁产生的4个必要条件之一即可。
若允许系统资源都能共享使用,则系统不会进入死锁状态。但有些资源根本不能同时访问,如打印机等临界资源只能互斥使用。所以,破坏互斥条件而预防死锁的方法不太可行,而且在有的场合应该保护这种互斥性。
当一个已保持了某些不可剥夺资源的进程请求新的资源而得不到满足时,它必须释放已经保持的所有资源,待以后需要时再重新申请。这意味着,一个进程已占有的资源会被暂时释放,或者说是被剥夺,或从而破坏了不剥夺条件。
该策略实现起来比较复杂,释放已获得的资源可能造成前一阶段工作的失效,反复地申请和释放资源会增加系统开销,降低系统吞吐量。这种方法常用于状态易于保存和恢复的资源,如 CPU的寄存器及内存资源,一般不能用于打印机之类的资源。
采用预先静态分配方法,即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不把它投入运行。一旦投入运行,这些资源就一直归它所有,不再提出其他资源请求,这样就可以保证系统不会发生死锁。
这种方式实现简单,但缺点也显而易见,系统资源被严重浪费,其中有些资源可能仅在运行初期或运行快结束时才使用,甚至根本不使用。而且还会导致“饥饿”现象,由于个别资源长期被其他进程占用时,将致使等待该资源的进程迟迟不能开始运行。
为了破坏循环等待条件,可采用顺序资源分配法。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源一次申请完。也就是说,只要进程提出申请分配资源 $R_i$ ,则该进程在以后的资源申请中就只能申请编号大于 $R_i$ 的资源。
这种方法存在的问题是,编号必须相对稳定,这就限制了新类型设备的增加;尽管在为资源编号时已考虑到大多数作业实际使用这些资源的顺序,但也经常会发生作业使用资源的顺序与系统规定顺序不同的情况,造成资源的浪费;此外,这种按规定次序申请资源的方法,也必然会给用户的编程带来麻烦。
避免死锁同样属于事先预防策略,但并不是事先采取某种限制措施破坏死锁的必要条件,而是在资源动态分配过程中,防止系统进入不安全状态,以避免发生死锁。这种方法所施加的限制条件较弱,可以获得较好的系统性能。
避免死锁的方法中,允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配的安全性。若此次分配不会导致系统进入不安全状态,则允许分配;否则让进程等待。
所谓安全状态,是指系统能按某种进程推进顺序( $P_1$ , $P_2$ ,…, $P_n$ )为每个进程 $P_i$ 分配其所需的资源,直至满足每个进程对资源的最大需求,使每个进程都可顺序完成。此时称 $P_i$ , $P_-2$ .…, $P_n$ 为安全序列。若系统无法找到一个安全序列,则称系统处于不安全状态。
假设系统中有三个进程 $P_1$ , $P_2$ ,和 $P_3$ ,共有12台磁带机。进程 $P_1$ 共需要10台磁带机, $P_2$ 和 $P_3$ 分别需要4台和9台。假设在 $T_0$ 时刻,进程 $P_1$ , $P_2$ 和 $P_3$ 已分别获得5台、2台和2台,尚有3台未分配,见表2.5。

在 $T_0$ 时刻是安全的,因为存在一个安全序列 $P_2$ , $P_1$ , $P_3$ ,只要系统按此进程序列分配资源,那么每个进程都能顺利完成。也就是说,当前可用磁带机为3台,先把3台磁带机分配给 $Р_2$ 以满足其最大需求, $P_2$ 结束并归还资源后,系统有5台磁带机可用;接下来给 $P_1$ 分配5台磁带机以满足其最大需求, $P_1$ 结束并归还资源后,剩余10台磁带机可用;最后分配7台磁带机给 $P_3$ ,这样$P_3$也能顺利完成。
若在 $T_0$ 时刻后,系统分配1台磁带机给 $P_3$ ,系统剩余可用资源数为2,此时系统进入不安全状态,因为此时已无法再找到一个安全序列。当系统进入不安全状态后,便可能导致死锁。例如,把剩下的2台磁带机分配给 $P_2$ 这样, $P_2$ 完成后只能释放4台磁带机,既不能满足 $P_1$ 又不能满足 $P_3$ ,致使它们都无法推进到完成,彼此都在等待对方释放资源,陷入僵局,即导致死锁。
并非所有的不安全状态都是死锁状态,但当系统进入不安全状态后,便可能进入死锁状态;反之,只要系统处于安全状态,系统便可避免进入死锁状态。
银行家算法是最著名的死锁避免算法,其思想是:把操作系统视为银行家,操作系统管理的资源相当于银行家管理的资金,进程向操作系统请求分配资源相当于用户向银行家贷款。操作系统按照银行家制定的规则为进程分配资源。进程运行之前先声明对各种资源的最大需求量,当进程在执行中继续申请资源时,先测试该进程已占用的资源数与本次申请的资源数之和是否超过该进程声明的最大需求量。若超过则拒绝分配资源,若未超过则再测试系统现存的资源能否满足该进程尚需的最大资源量,若能满足则按当前的申请量分配资源,否则也要推迟分配。
可利用资源向量Available:含有m个元素的数组,其中每个元素代表一类可用的资源数目。Available[ j ]=K表示系统中现有 $R_j$ 类资源K个。
最大需求矩阵 Max: n×m 矩阵,定义系统中 $n$ 个进程中的每个进程对 $m$ 类资源的最大需求。简单来说,一行代表一个进程,一列代表一类资源。Max[ i,j ]=K表示进程i需要 $R_j$ 类资源的最大数目为K。
分配矩阵Allocation: $n \times m$ 矩阵,定义系统中每类资源当前已分配给每个进程的资源数。Allocation[ i,j ]=K表示进程 $i$ 当前已分得 $R_j$ 类资源的数目为K。初学者容易混淆Available向量和Allocation矩阵,在此特别提醒。
需求矩阵Need: $n \times m$ 矩阵,表示每个进程接下来最多还需要多少资源。Need[ i,j ]=K表示进程i还需要 $R_j$类资源的数目为K。
上述三个矩阵间存在下述关系:
Need= Max- Allocation
一般情况下,在银行家算法的题目中,Max矩阵和Allocation矩阵是已知条件,而求出 Need矩阵是解题的第一步。



前面介绍的死锁预防和避免算法,都是在为进程分配资源时施加限制条件或进行检测,若系统为进程分配资源时不采取任何措施,则应该提供死锁检测和解除的手段。
系统死锁可利用资源分配图来描述。如图2.13所示,用圆圈代表一个进程,用框代表一类资源。由于一种类型的资源可能有多个,因此用框中的一个圆代表一类资源中的一个资源。从进程到资源的有向边称为请求边,表示该进程申请一个单位的该类资源;从资源到进程的边称为分配边,表示该类资源已有一个资源分配给了该进程。
在图2.13所示的资源分配图中,进程 $P_1$ 已经分得了两个 $R_1$ 资源,并又请求一个 $R_2$ 资源;进程 $P_2$ ,分得了一个 $R_1$ 资源和一个 $R_2$ 资源,并又请求一个 $R_1$ 资源。

简化资源分配图可检测系统状态S是否为死锁状态。简化方法如下:
1)在资源分配图中,找出既不阻塞又不孤点的进程 $P_i$ (即找出一条有向边与它相连,且该有向边对应资源的申请数量小于等于系统中己有的空闲资源数量,如在图2.13中, $R_1$ 没有空闲资源, $R_2$ 有一个空闲资源。若所有连接该进程的边均满足上述条件,则这个进程能继续运行直至完成,然后释放它所占有的所有资源)。消去它所有的请求边和分配边,使之成为孤立的结点。在图2.14(a)中, $P_1$ 是满足这一条件的进程结点,将P的所有边消去,便得到图2.14(b)所示的情况。
这里要注意一个问题,判断某种资源是否有空间,应用它的资源数量减去它在资源分配图中的出度,例如在图2.13中, $R_1$ 的资源数为3,而出度也为3,所以 $R_1$ 没有空闲资源, $R_2$ 的资源数为2,出度为1,所以 $R_2$ 有一个空闲资源。
2)进程 $P_i$ 所释放的资源,可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。在图2.13中,进程 $P_2$ 就满足这样的条件。根据1)中的方法进行一系列简化后,若能消去图中所有的边,则称该图是可完全简化的,如图2.14(c)所示。

S为死锁的条件是当且仅当S状态的资源分配图是不可完全简化的,该条件为 $\color{green}{\text{死锁定理}}$ 。
一旦检测出死锁,就应立即采取相应的措施来解除死锁。死锁解除的主要方法有:
1)资源剥夺法。挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但应防止被挂起的进程长时间得不到资源而处于资源匮乏的状态。
2)撤销进程法。强制撤销部分甚至全部死锁进程并剥夺这些进程的资源。撤销的原则可以按进程优先级和撤销进程代价的高低进行。
3)进程回退法。让一(或多〉个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而非被剥夺。要求系统保持进程的历史信息,设置还原点。
本节开头提出的问题的参考答案如下。
1)为什么会产生死锁?产生死锁有什么条件?
由于系统中存在一些不可剥夺资源,当两个或两个以上的进程占有自身的资源并请求对方的资源时,会导致每个进程都无法向前推进,这就是死锁。死锁产生的必要条件有4个,分别是互斥条件、不剥夺条件、请求并保持条件和循环等待条件。
互斥条件是指进程要求分配的资源是排他性的,即最多只能同时供一个进程使用。
不剥夺条件是指进程在使用完资源之前,资源不能被强制夺走。
请求并保持条件是指进程占有自身本来拥有的资源并要求其他资源。
循环等待条件是指存在一种进程资源的循环等待链。
2)有什么办法可以解决死锁问题?
死锁的处理策略可以分为预防死锁、避免死锁及死锁的检测与解除。
死锁的预防是指通过设立一些限制条件,破坏死锁的一些必要条件,让死锁无法发生。
死锁的避免是指在动态分配资源的过程中,用一些算法防止系统进入不安全状态,从而避免死锁。
死锁的检测和解除是指在死锁产生前不采取任何措施,只检测当前系统有没有发生死锁,若有,则采取一些措施解除死锁。
1)进程是程序及其数据在计算机上的一次运行活动,是一个动态的概念。进程的运行实体是程序,离开程序的进程没有存在的意义。从静态角度看,进程是由程序、数据和进程控制块(PCB)三部分组成的。而程序是一组有序的指令集合,是一种静态的概念。
2)进程是程序的一次执行过程,它是动态地创建和消亡的,具有一定的生命周期,是暂时存在的;而程序则是一组代码的集合,是永久存在的,可长期保存。
3)一个进程可以执行一个或几个程序,一个程序也可构成多个进程。进程可创建进程,而程序不可能形成新的程序。
4)进程与程序的组成不同。进程的组成包括程序、数据和PCB。
具有等待队列的信号量的实现可能导致这样的情况:两个或多个进程无限地等待一个事件,而该事件只能由这些等待进程之一来产生。这里的事件是V操作的执行(即释放资源)。出现这样的状态时,这些进程称为死锁(Deadlocked)。
为加以说明,考虑一个由两个进程 $P_0$ 和 $P_1$ 组成的系统,每个进程都访问两个信号量S和Q,这两个信号量的初值均为1。

假设进程 $P_0$ 执行P(S),接着进程 $P_1$ 执行P(Q)。当进程 $P_0$ 执行P(Q)时,它必须等待,直到进程 $P_1$ 执行V(Q)。类似地,当进程 $P_1$ 执行P(S)时,它必须等待,直到进程 $P_0$ 执行V(S)。由于这两个V操作都不能执行,因此进程 $P_0$ 和进程 $P_1$ 就死锁了。
一组进程处于死锁状态是指组内的每个进程都在等待一个事件,而该事件只可能由组内的另一个进程产生。这里所关心的主要是事件是资源的获取和释放。
与死锁相关的另一个问题是无限期阻塞(Indefinite Blocking)或饥饿(Starvation),即进程在信号量内无穷等待的情况。
产生饥饿的主要原因是:在一个动态系统中,对于每类系统资源,操作系统需要确定一个分配策略,当多个进程同时申请某类资源时,由分配策略确定资源分配给进程的次序。有时资源分配策略可能是不公平的,即不能保证等待时间上界的存在。在这种情况下,即使系统没有发生死锁,某些进程也可能会长时间等待。当等待时间给进程推进和响应带来明显影响时,称发生了进程“饥饿”,当“饥饿”到一定程度的进程所赋予的任务即使完成也不再具有实际意义时,称该进程被“饿死”。
例如,当有多个进程需要打印文件时,若系统分配打印机的策略是最短文件优先,则长文件的打印任务将由于短文件的源源不断到来而被无限期推迟,导致最终“饥饿”甚至“饿死”。
“饥饿”并不表示系统一定会死锁,但至少有一个进程的执行被无限期推迟。“饥饿”与死锁的主要差别如下:
1)进入“饥饿”状态的进程可以只有一个,而因循环等待条件而进入死锁状态的进程却必须大于等于两个。
2)处于“饥饿”状态的进程可以是一个就绪进程,如静态优先权调度算法时的低优先权进程,而处于死锁状态的进程则必定是阻塞进程。
银行家算法的主要思想是避免系统进入不安全状态。在每次进行资源分配时,它首先检查系统是否有足够的资源满足要求,若有则先进行分配,并对分配后的新状态进行安全性检查。若新状态安全,则正式分配上述资源,否则拒绝分配上述资源。这样,它保证系统始终处于安全状态,从而避免了死锁现象的发生。
并发进程的执行会产生相互制约的关系:一种是进程之间竞争使用临界资源,只能让它们逐个使用,这种现象称为互斥,是一种竞争关系;另一种是进程之间协同完成任务,在关键点上等待另一个进程发来的消息,以便协同一致,是一种协作关系。
进程是系统资源的使用者,系统的资源大部分都是以进程为单位分配的。而用户使用计算机是为了实现一串相关的任务,通常把用户要求计算机完成的这一串任务称为作业。
批处理系统可以通过磁记录设备或卡片机向系统提交批作业,由系统的SPOOLing输入进程将作业放入磁盘的输入井,作为后备作业。作业调度程序(一般也作为独立的进程运行)每当选择一道后备作业运行时,首先为该作业创建一个进程(称为该作业的根进程)。该进程将执行作业控制语言解释程序,解释该作业的作业说明书。父进程在运行过程中可以动态地创建一个或多个子进程,执行说明书中的语句。例如,对一条编译的语句,该进程可以创建一个子进程执行编译程序对用户源程序进行编译。类似地,子进程也可继续创建子进程去完成指定的功能。因此,一个作业就动态地转换成了一组运行实体—进程族。当父进程遇到作业说明书中的“撤出作业”语句时,将该作业从运行态改变为完成态,将作业及相关结果送入磁盘上的输出井。作业终止进程负责将输出井中的作业利用打印机输出,回收作业所占用的资源,删除作业有关的数据结构,删除作业在磁盘输出井中的信息等。作业终止进程撤除一道作业后,可向作业调度进程请求进行新的作业调度。至此,一道进入系统运行的作业全部结束。
在分时系统中,作业的提交方法、组织形式均与批处理作业有很大差异。分时系统的用户通过命令语言逐条与系统应合八把武大系体自动时,系统为每个终端设备建立一个进程(称为终端统内部对应一个(以右T经程序,命令解释程序从终端设备读入俞令,解藉疯令是一茶后台命进程),该进程执仃类令n可以创建一个子进程去具体执行。若当HPN根据需要创建子孙进程。条命令。对于每条终端命令,可以创建一个子进程去具体执行。若当前的终端命令是一条后台命令,则可以和下一条终端命令并行处理。各子进程在运行过程中完全可以根据需要创建子孙进程。终端命令所对应的进程结束后,命令的功能也相应处理完毕。用户本次上机完毕,用户通过一条登出命令即结束上机过程。
分时系统的作业就是用户的一次上机交互过程,可以认为终端进程的创建是一个交互作业的开始,登出命令运行结束代表用户交互作业的终止。
命令解释程序流程扮演着批处理系统中作业控制语言解释程序的角色,只不过命令解释程序是从用户终端接收命令。
在同时支持交互和批处理的操作系统中,人们可以用交互的方式准备好批作业的有关程序、数据及作业控制说明书。比如,可用交互式系统提供的全屏幕编辑命令编辑好自编的一个天气预报程序,用编译及装配命令将程序变成可执行文件,用调试命令进行程序调试。调试成功后,用户每天都要做如下工作:准备原始天气数据,运行天气预报执行文件处理原始数据,把结果打印出来等。这时,用交互系统提供的全屏幕编辑命令编辑好将要提交的作业控制说明书文件,如Windows系统的 bat 文件和 Linux系统的sh文件。然后用一条作业提交命令将作业提交到系统作业队列中。系统有专门的作业调度进程负责从作业队列中选择作业,为被选取的作业创建一个父进程运行命令解释程序,解释执行作业控制说明书文件中的命令。